zet-lib 1.3.39 → 1.3.40

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/lib/zReport.js CHANGED
@@ -1,982 +1,982 @@
1
- const axios = require('axios')
2
- const fs = require('fs-extra')
3
- const nodemailer = require('nodemailer')
4
- const qs = require('qs')
5
- const moment = require('moment')
6
- const XLSX = require('xlsx')
7
- const Excel = require('exceljs')
8
- const debug = require('./debug')
9
- const zRoute = require('./zRoute')
10
- const Util = require('./Util')
11
- const connection = require('./connection')
12
- const io = require('./io')
13
- const config = require('dotenv').config()
14
- const zReport = {}
15
- const myCache = require('./cache')
16
-
17
- /*
18
- Custom Reports
19
- Report Generator
20
- */
21
-
22
- /*
23
- UI For edit excel File
24
- */
25
- zReport.reportData = (filename, data, sessions = {}) => {
26
- console.log(filename)
27
- const workbook = XLSX.readFile(filename)
28
- const Sheets = workbook.Sheets
29
- let sheet = Sheets[Object.keys(Sheets)[0]]
30
- let excel = Util.excelSequence()
31
- let ref = sheet['!ref']
32
- let explode = ref.split(':')
33
- let lastchar = explode[1]
34
- let stringPattern = /[A-Z]+/i
35
- let digitPattern = /[0-9]+/i
36
- let stringColumn = lastchar.match(stringPattern)[0]
37
- let maxColumn = excel.indexOf(stringColumn)
38
-
39
- let maxRow = lastchar.match(digitPattern)[0]
40
- //add 5 row for input cells
41
- maxRow = parseInt(maxRow) + 5
42
- let excelData = {}
43
- let excelValue = {}
44
- let tableModel = {}
45
- let excelQuery = data.excel_query || {}
46
- let dataForExcel = excelQuery.excel
47
- let dataCallback = data.callback || []
48
- let callback = {}
49
- let objQuery = {}
50
- let results = []
51
-
52
- //set callback as object
53
- dataCallback.map((m) => {
54
- callback[m.name] = m.value
55
- })
56
-
57
- //console.log(JSON.stringify(sessions));
58
-
59
- //create UI Button in excel cell
60
- if (dataForExcel) {
61
- dataForExcel.forEach((datafor, index) => {
62
- let button = '',
63
- name = datafor.name,
64
- callback = datafor.callback || '',
65
- value = datafor.value || ''
66
- let split = value.split('.')
67
- let len = split.length
68
- let MYMODEL = {}
69
- let table = '',
70
- tableKey = ''
71
- if (len == 4) {
72
- table = split[2]
73
- tableKey = split[3]
74
- } else {
75
- table = split[0]
76
- tableKey = split[1]
77
- }
78
-
79
- const MYMODELS = zRoute.MYMODELS() || {}
80
- MYMODEL = tableModel.hasOwnProperty(table) ? tableModel[table] : MYMODELS[table]
81
- if (!objQuery[table]) {
82
- objQuery[table] = []
83
- }
84
- objQuery[table].push(value)
85
- if (!excelValue) {
86
- excelValue[name] = ''
87
- } else {
88
- button = excelValue[name] || ''
89
- }
90
-
91
- let labels = value.indexOf('_SESSIONS_') > -1 ? value.replace('_SESSIONS_.', '') : tableKey
92
- button += `<li class="dragged"> <button class="btn btn-info btn-excel" type="button" title="${name.replace('[', '').replace(']', '')} : ${value}">${MYMODEL.labels[labels]}
93
- <input type="hidden" class="EXCEL" name="${name}" value='${value}_CALLBACK_SEPARATOR_${callback}'> <i class="fa fa-code call-me"></i> <i class="fa fa-trash trash-me"></i></button></li>`
94
- excelValue[name] = button
95
- })
96
- }
97
- //end UI
98
-
99
- let table = `<table class="table table-striped table-bordered" >`
100
- for (var x = 1; x < maxRow; x++) {
101
- table += `<tr>`
102
- let arr = []
103
- for (var i = 0; i <= maxColumn; i++) {
104
- let str = excel[i] + x
105
- let strkey = excel[i] + '[' + x + ']'
106
- let defaultValue = Object.prototype.hasOwnProperty.call(sheet, str) ? sheet[str].v : ''
107
- let value = excelValue[strkey] || defaultValue
108
- table += `<td width="80px" id="${str}" title="${str}" class="mydragable" data-col="${excel[i]}" data-row="${x}">${value}</td>`
109
- arr.push(value)
110
- }
111
- table += `</tr>`
112
- results.push(arr)
113
- }
114
- table += `</table>`
115
- return {
116
- table: table,
117
- excelData: excelData,
118
- results: results,
119
- max: maxRow,
120
- objQuery: objQuery,
121
- }
122
- }
123
-
124
- //Save reports Setup
125
- zReport.reportPostData = async (req, res) => {
126
- let data_status = Util.jsonSuccess('Success')
127
- try {
128
- let body = req.body
129
- ///console.log(body)
130
- let post = zRoute.postData(body)
131
- let ex = post.excel
132
- delete post.excel
133
- let id = body.id
134
- let CALLBACK_SEPARATOR = '_CALLBACK_SEPARATOR_'
135
-
136
- //remove CALLBACK in query
137
- //sometimes bug in query callback separator in javascript
138
- let query = body.query
139
- query = Util.replaceAll(query, CALLBACK_SEPARATOR, '')
140
- let callback = [],
141
- excel = []
142
- ex.map((e) => {
143
- let name = e.name || ''
144
- let value = e.value || ''
145
- //console.log(value);
146
- if (value.indexOf(CALLBACK_SEPARATOR) > -1) {
147
- let split = value.split(CALLBACK_SEPARATOR)
148
- excel.push({
149
- name: name,
150
- title: name.replace('[', '').replace(']', ''),
151
- value: split[0],
152
- callback: Util.replaceAll(split[1], "'", '"'),
153
- })
154
- }
155
- })
156
- post.excel = excel || ''
157
- post.query = query || ''
158
- await connection.update({
159
- table: 'zreport',
160
- data: { json: JSON.stringify(post) },
161
- where: {
162
- id: id,
163
- },
164
- })
165
- } catch (err) {
166
- debug(req, res, err)
167
- data_status = Util.flashError(err.toString())
168
- }
169
- res.json(data_status)
170
- }
171
-
172
- //SUBMIT REPORT ROUTE FOR REPORT GENERATOR
173
- zReport.submitReport = async (req, res) => {
174
- let company_id = res.locals.companyId
175
- let body = req.body
176
- console.log('BODYYYYYY')
177
- console.log(body)
178
- delete body._csrf
179
- const MYMODELS = zRoute.MYMODELS() || {}
180
- //delete key where null
181
- for (let keys in body) {
182
- if (body[keys] == '') {
183
- delete body[keys]
184
- }
185
- if (body[keys] == 'undefined') {
186
- delete body[keys]
187
- }
188
- }
189
- //console.log(body);
190
- let $session = req.session.user
191
- try {
192
- let reportId = body.zreport_name
193
- let row = await connection.result({
194
- table: 'zreport',
195
- where: {
196
- id: reportId,
197
- },
198
- })
199
- let json = row.json ? row.json : {}
200
- console.log(json)
201
- let query = json.query || ''
202
- let table = json.tables[0] || ''
203
- if (!table) {
204
- return res.json('error not configure!')
205
- }
206
- let title = row.title
207
- let jsonExcels = json.excel || []
208
- let excelObj = {}
209
- let callback = {}
210
- jsonExcels.map((jsonExcel) => {
211
- if (!excelObj[jsonExcel.name]) excelObj[jsonExcel.name] = []
212
-
213
- let jsonvalue = Util.replaceAll(jsonExcel.value, '.', '___')
214
- let nameCallback = jsonExcel.title + '___' + jsonvalue
215
-
216
- jsonExcel.callbackName = nameCallback
217
-
218
- let split = jsonvalue.split('___')
219
- if (split.length == 2) {
220
- jsonExcel.data = jsonvalue
221
- } else if (split.length == 3) {
222
- jsonExcel.data = split[0] + '___' + split[1]
223
- } else {
224
- jsonExcel.data = jsonvalue
225
- }
226
-
227
- if (jsonExcel.callback) callback[nameCallback] = jsonExcel.callback
228
-
229
- excelObj[jsonExcel.name].push(jsonExcel)
230
- })
231
-
232
- delete body.zreports_name
233
-
234
- //console.log(filterWhere);
235
- //excel instance
236
- var workbook = new Excel.Workbook()
237
- let filename = `${dirRoot}/public/uploads/zreport/${row.excel}`
238
- await workbook.xlsx.readFile(filename)
239
- var worksheet = workbook.worksheets[0] //the first one;
240
- let images = [] // store images
241
- // Force workbook calculation on load
242
- //workbook.calcProperties.fullCalcOnLoad = true;
243
-
244
- //set images
245
- var setExcelImages = (script, data, r, c) => {
246
- let excelsq = Util.excelSequence()
247
- //A1 = col=0 row=0
248
- //convert column to integer
249
- c = excelsq.indexOf(c)
250
- r = r - 1
251
- script = script || ''
252
- if (script == '') {
253
- return ''
254
- }
255
- data = data || ''
256
- if (data == '') {
257
- return ''
258
- }
259
-
260
- let str = script.replace('hasImages(', '').replace(')', '')
261
- let explode = str.split(',')
262
- let table = explode[0].trim() || ''
263
- let width = parseInt(explode[1]) || 200
264
- let height = parseInt(explode[2]) || 200
265
- let photoName = `${dirRoot}/public/uploads/${table}/${data}`
266
- if (!fs.existsSync(photoName)) {
267
- return ''
268
- }
269
- let image = workbook.addImage({
270
- filename: photoName,
271
- extension: data.split('.').pop(),
272
- })
273
- images.push({
274
- image: image,
275
- width: width,
276
- height: height,
277
- c: c,
278
- r: r,
279
- })
280
-
281
- return ''
282
- }
283
- //end set images
284
-
285
- //query where sql;
286
- //query executes
287
- console.log('query')
288
- console.log(JSON.stringify(query))
289
- console.log('query')
290
-
291
- let datas = []
292
- if (query) {
293
- var wheres = []
294
- var whereArr = []
295
- var where = ''
296
- //TODO if has order by limit
297
- var sql = json.query
298
- console.log('sql')
299
- console.log(sql)
300
- console.log('sql')
301
-
302
- var num = 1
303
- for (var key in json.filter) {
304
- if (body[key]) {
305
- wheres.push(Util.replaceAll(json.filter[key], '?', `$${num}`))
306
- if (json.filter[key].includes('LIKE')) {
307
- whereArr.push(`%${body[key]}%`)
308
- } else {
309
- whereArr.push(body[key])
310
- }
311
- num++
312
- }
313
- }
314
-
315
- //row.wheres from database
316
- var rowWheres = row.wheres || []
317
- rowWheres.forEach(function (item) {
318
- wheres.push(item)
319
- })
320
-
321
- if (wheres.length) {
322
- where += ' WHERE '
323
- where += wheres.join(' AND ')
324
- }
325
- var orderby = ''
326
- if (row.orderby) {
327
- orderby = ` ORDER BY ${row.orderby} `
328
- }
329
- sql = sql + where + orderby
330
- console.log('sql')
331
- console.log(sql)
332
- console.log('sql')
333
-
334
- console.log('whereArr')
335
- console.log(JSON.stringify(whereArr))
336
- console.log('whereArr')
337
- datas = await connection.query(sql, whereArr)
338
- }
339
-
340
- let excelMap = {}
341
- let beginLooping = 0
342
- for (let keys in excelObj) {
343
- let split = keys.split('[')
344
- let column = split[0]
345
- let idx = parseInt(split[1])
346
-
347
- if (!excelMap[keys]) excelMap[keys] = {}
348
-
349
- excelMap[keys].column = column
350
- excelMap[keys].row = idx
351
- excelMap[keys].rowColumn = column + idx
352
- if (beginLooping == 0) beginLooping = idx
353
- }
354
-
355
- console.log('datas')
356
- console.log(JSON.stringify(datas))
357
- console.log('datas')
358
-
359
- if (datas.length > 1) {
360
- try {
361
- for (var i = 0; i < datas.length - 1; i++) {
362
- let r = i + beginLooping
363
- worksheet.spliceRows(r + 1, 1, true)
364
- worksheet.duplicateRow(r, 1, true)
365
- }
366
- } catch (e) {
367
- console.log('no style')
368
- }
369
- }
370
-
371
- var ioroom = function (err) {
372
- io.to(res.locals.user.token).emit('error', err.toString())
373
- }
374
-
375
- datas.forEach((data, index) => {
376
- for (let keys in excelObj) {
377
- let columnIndex = excelMap[keys].column
378
- let rowIndex = parseInt(excelMap[keys].row) + index
379
- let rowColumn = columnIndex + rowIndex
380
- let value = excelObj[keys]
381
- .map((m) => {
382
- //console.log(m)
383
- return callback.hasOwnProperty(m.callbackName) ? (callback[m.callbackName].indexOf('hasImages') > -1 ? setExcelImages(callback[m.callbackName], data[m.data], rowIndex, columnIndex) : zReport.callbackReport(callback[m.callbackName], data[m.data], rowIndex, columnIndex, null, data, ioroom, res)) : data[m.data]
384
- })
385
- .join(' ')
386
-
387
- worksheet.getCell(rowColumn).value = Util.isNumeric(value) ? parseFloat(value) : value
388
- }
389
- })
390
- //build images if any
391
- images.map((m) => {
392
- worksheet.addImage(m.image, {
393
- tl: { col: m.c, row: m.r },
394
- ext: { width: m.width, height: m.height },
395
- })
396
- })
397
-
398
- var fileName = title + '.xlsx'
399
- if (row.scripts) {
400
- return zReport.additionalScripts(req, res, row.scripts, fileName, datas, moment, Util, dirRoot, ioroom, connection, worksheet, workbook, debug)
401
- } else {
402
- res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
403
- res.setHeader('Content-Disposition', 'attachment; filename=' + fileName)
404
- await workbook.xlsx.write(res)
405
- res.end()
406
- }
407
- } catch (e) {
408
- console.log(e)
409
- debug(req, res, e)
410
- res.json(e.toString())
411
- }
412
- }
413
-
414
- zReport.additionalScripts = (req, res, script, fileName, datas, moment, Util, dirRoot, ioroom, connection, worksheet, workbook) => {
415
- script = script.trim() || ''
416
- let CALL = {
417
- Util: Util,
418
- moment: moment,
419
- datas: datas,
420
- dirRoot: dirRoot,
421
- ioroom: ioroom,
422
- res: res,
423
- req: req,
424
- fileName: fileName,
425
- connection: connection,
426
- worksheet: worksheet,
427
- workbook: workbook,
428
- }
429
- let STRINGS_VARIABLE = ''
430
- Object.keys(CALL).forEach(function (item) {
431
- STRINGS_VARIABLE += ` var ${item} = this.${item}; `
432
- })
433
- return Function(`
434
- var runcode = async() => {
435
- ${STRINGS_VARIABLE}
436
- try {
437
- ${script};
438
- res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
439
- res.setHeader('Content-Disposition', 'attachment; filename=' + fileName);
440
- await workbook.xlsx.write(res);
441
- res.end();
442
- } catch(e) {
443
- console.log(e.toString());
444
- res.json(e.toString());
445
- }
446
- };
447
- runcode();
448
-
449
- `).call(CALL)
450
- }
451
-
452
- zReport.callbackReport = (script, data, row, column, selectFromTable, obj, ioroom, res) => {
453
- script = script.trim() || ''
454
- let results = {}
455
- for (var key in obj) {
456
- let name = key.split('___')
457
- if (name.length > 0) {
458
- results[name[1]] = obj[key]
459
- }
460
- }
461
-
462
- let first = script.charAt(0)
463
- let CALL = {
464
- Util: Util,
465
- obj: obj,
466
- moment: moment,
467
- data: data,
468
- dirRoot: dirRoot,
469
- selectFromTable: selectFromTable,
470
- row: row,
471
- col: column,
472
- results: results,
473
- ioroom,
474
- res: res,
475
- }
476
- let STRINGS_VARIABLE = ''
477
- Object.keys(CALL).forEach(function (item) {
478
- STRINGS_VARIABLE += ` var ${item} = this.${item}; `
479
- })
480
- return Function(`
481
- ${STRINGS_VARIABLE}
482
- try {
483
- ${script};
484
- } catch(e) {
485
- console.log(e);
486
- ioroom("CELLS : " + col+row+ " " +e.toString());
487
- }
488
- `).call(CALL)
489
- }
490
-
491
- zReport.staticDataReport = (data, filter) => {
492
- delete data.id
493
- delete data.password
494
- delete data.token
495
- let session_obj = {}
496
- let session_obj_fields = {}
497
-
498
- filter.map((m) => {
499
- let keyName = m.name
500
- session_obj[keyName] = m.label
501
- session_obj_fields[keyName] = {
502
- name: keyName,
503
- title: m.label,
504
- type: 'text',
505
- category: 'text',
506
- length: 'undefined',
507
- required: false,
508
- search: 'LIKE',
509
- key: '',
510
- placeholder: m.label,
511
- }
512
- })
513
-
514
- for (var key in data) {
515
- let keyName = '$session.' + key
516
- session_obj[keyName] = key
517
- session_obj_fields[keyName] = {
518
- name: keyName,
519
- title: key,
520
- type: 'text',
521
- category: 'text',
522
- length: 'undefined',
523
- required: false,
524
- search: 'LIKE',
525
- key: '',
526
- placeholder: key,
527
- }
528
-
529
- if (key == 'company') {
530
- for (var k in data[key]) {
531
- let keyName2 = '$session.company.' + k
532
- session_obj[keyName2] = k
533
- session_obj_fields[keyName2] = {
534
- name: keyName2,
535
- title: key + ' ' + k,
536
- type: 'text',
537
- category: 'text',
538
- length: 'undefined',
539
- required: false,
540
- search: 'LIKE',
541
- key: '',
542
- placeholder: key + ' ' + k,
543
- }
544
- }
545
- }
546
- }
547
-
548
- return {
549
- table: '_SESSIONS_',
550
- routeName: '_SESSIONS_',
551
- keys: Object.keys(session_obj),
552
- labels: session_obj,
553
- fields: session_obj_fields,
554
- relations: {},
555
- dropdowns: {},
556
- modules: {},
557
- }
558
- }
559
-
560
- zReport.dataHTMLList = async () => {
561
- let html = ''
562
- let results = await connection.results({
563
- table: 'zreports',
564
- whereArray: [{ field: 'parent_id', value: 'IS NULL', type: 'inline' }],
565
- order_by: ['id desc'],
566
- })
567
- for (const result of results) {
568
- let myfilter = await zReport.filters2(result.filter, result.company_id)
569
- let childReport = ''
570
- let rows = await connection.results({
571
- table: 'zreports',
572
- where: {
573
- parent_id: result.id,
574
- },
575
- })
576
- rows.forEach((row, index) => {
577
- let action = ''
578
- if (row.excel) {
579
- action += `<button title="setup Report" onclick="location.href='/zreports/setup/${row.id}'" data-id="${row.id}" class="btn image-button boxy-small" type="button"><img src="/assets/icons/settings-filled.svg" class="icons-bg-black"></button>`
580
- }
581
- action += `<button title="edit Report" onclick="location.href='/zreports/update/${row.id}'" class="btn image-button boxy-small" type="button"><img src="/assets/icons/pencil.svg" class="icons-bg-black"></button>`
582
- action += `<button title="delete Report" onclick="deletereport('${row.id}')" data-id="${row.id}" class="btn image-button boxy-small" type="button"><img src="/assets/icons/trash.svg" class="icons-bg-black"></button>`
583
- childReport += `<tr><td>${index + 1}. </td><td>${row.title}</td><td><a href="/uploads/zreports/${row.excel}">Excel File</a></td><td>${action}</td></tr>`
584
- })
585
-
586
- html += `<div class="col-xs-12 col-md-6">
587
- <div class="boxy bottom-down">
588
- <div class="float">
589
- <span class="icon-small icons-primary" onclick="location.href='/zreports/update/${result.id}'" title="${LANGUAGE.update}"><img class="icons-bg-white gridupdate icon-image" src="/assets/icons/edit.svg"></span>
590
-
591
- <span class="icon-small icons-danger float-end" onclick="deletereport('${result.id}')" title="${LANGUAGE.delete}"><img class="icons-bg-white griddelete icon-image" src="/assets/icons/trash-filled.svg"></span>
592
- </div>
593
-
594
- <div class="container-fluid">
595
- <h2 class="text text-center text-capitalize">Custom Reports: ${result.title}</h2>
596
- <p class="card-text card-text text-center text-muted">${result.description}</p>
597
- <div class="card">
598
- <div class="card-body">
599
- <a class="card-link" href="${process.env.APP_URL}/zreports/show/${result.id}" target="_blank">${process.env.APP_URL}/zreports/show/${result.id}</a>
600
-
601
- <div class="float-end">
602
- <img src="/assets/icons/copy.svg" class="icons-bg-black cursor" onclick="copyToClipboard('${process.env.APP_URL}/zreports/show/${result.id}')" alt="Copy"></div>
603
- </div>
604
- </div>
605
-
606
- <hr>
607
- <form method="post" action="/zreports">${myfilter}
608
- <button type="submit" class="btn btn-block btn-success mt-3">Submit </button>
609
- </form>
610
- <hr>
611
- <div class="form-group divReportList">
612
- <div class="panel panel-default">
613
- <div class="panel-heading">Report List</div>
614
- <div class="panel-body">
615
- <table class="table table-hover">
616
- <thead>
617
- <tr>
618
- <th width="3%">#</th>
619
- <th width="50%">Name</th>
620
- <th>File</th>
621
- <th> <button title="add Report" data-id="${result.id}" onclick="$('#parent_id').val(${result.id})" data-bs-toggle="modal" data-bs-target="#reportModal" class="btn image-button boxy-small float-end" type="button"><img src="/assets/icons/plus.svg" class="icons-bg-black"></button></th>
622
- </tr>
623
- </thead>
624
- <tbody class="body-reportList" data-value="">${childReport}</tbody>
625
- </table>
626
- </div>
627
- </div>
628
- </div>
629
-
630
- </div>
631
- </div>
632
- </div>`
633
- }
634
- return html
635
- }
636
-
637
- zReport.listReports = async (data) => {
638
- let html = ''
639
- let rows = await connection.query('select * from zreport where parent_id = ?', [data.id])
640
- rows.forEach((obj, index) => {
641
- let action = ''
642
- if (obj.excel) {
643
- action += `<a href="/zreport/setup/${obj.id}" class="btn btn-default"> <i class="fa fa-gear" title="Setup Report"></i> </a>`
644
- }
645
- action += `<a href="/zreport/update/${obj.id}" class="btn btn-default"><i class="fa fa-pencil" title="Edit Data"></i> </a>`
646
- action += `<a href="#" class="btn btn-default reportdelete" data-id="${obj.id}"><i class="fa fa-trash " title="Delete Report"></i></a>`
647
- html += `<tr><td>${index + 1}. </td><td>${obj.title}</td><td><a href="/uploads/zreport/${obj.excel}">Excel File</a></td><td>${action}</td></tr>`
648
- })
649
-
650
- return `<div class="form-group divReportList">
651
- <div class="panel panel-default">
652
- <div class="panel-heading">Report List</div>
653
- <div class="panel-body">
654
- <table class="table table-hover">
655
- <thead>
656
- <tr>
657
- <th width="3%">#</th>
658
- <th width="50%">Name</th>
659
- <th>File</th>
660
- <th class="pull-right"><a href="/zreport/create?id=${data.id}" class="btn btn-default" title="Add" id="addReportList"><i class="fa fa-plus"></i></a></th>
661
- </tr>
662
- </thead>
663
- <tbody class="body-reportList" data-value="">${html}</tbody>
664
- </table>
665
- </div>
666
- </div>
667
- </div>`
668
- }
669
-
670
- zReport.filterReport = async (req, res) => {
671
- let id = req.params.id || ''
672
- let companyId = res.locals.companyId
673
- let data = [],
674
- rows = []
675
- if (id == '') {
676
- rows = await connection.query('select * from zreports where company_id = ? and parent_id IS NULL', [companyId])
677
- } else {
678
- rows = await connection.results({ table: 'zreports', where: { id: id, company_id: companyId } })
679
- }
680
-
681
- console.log(rows)
682
- for (var i = 0; i < rows.length; i++) {
683
- var row = rows[i]
684
- //rows.forEach(async function (row) {
685
- let reportsRow = await connection.results({ table: 'zreports', where: { parent_id: row.id } })
686
- console.log(reportsRow)
687
- let filterHtml = reportsRow.length > 0 ? `<form method="post" action="/zreport" >` : ''
688
- let filters = JSON.parse(row.filter) || []
689
- console.log('filters :')
690
- console.log(filters)
691
- row.listReports = await zReport.listReports(row)
692
- for (var x = 0; x < filters.length; x++) {
693
- let filter = filters[x]
694
- let options = `<option value="">Please Select</option>`
695
- let html = ''
696
- if (filter.type == 1) {
697
- html = `<input type="text" class="datepicker form-control" name="${filter.name}" >`
698
- } else if (filter.type == 2) {
699
- let values = JSON.parse(filter.value) || []
700
- values.map(function (e) {
701
- options += `<option value="${e.key}">${e.label}</option>`
702
- })
703
- html = `<select class="form-control form-select" name="${filter.name}">${options}</select>`
704
- } else if (filter.type == 3) {
705
- let values = filter.value || ''
706
- let explode = values.split('.')
707
- let table = explode[0]
708
- if (table && fs.existsSync(dirRoot + '/models/' + table + '.js')) {
709
- let keycolumn = explode[1]
710
- let labelcolumn = explode[2]
711
- let MYMODEL = require('./../models/' + table)
712
- let fields = MYMODEL.keys
713
- let where = ''
714
- if (fields.indexOf('company_id') > -1) {
715
- where = ` where company_id = ${company_id} `
716
- }
717
- let results = await connection.query('select * from `' + table + '` ' + where + ' order by ' + labelcolumn + ' ASC')
718
- results.map(async (result) => {
719
- options += `<option value="${result[keycolumn]}">${result[labelcolumn]}</option>`
720
- })
721
- html = `<select class="form-control form-select" name="${filter.name}">${options}</select>`
722
- }
723
- }
724
- filterHtml += `<div class="row"><div class="form-group"><label class="control-label col-md-2">${filter.label}</label><div class="col-md-10">${html}</div></div></div><br>`
725
- row.filterHtml = filterHtml
726
- }
727
-
728
- //drop down report name
729
- let reportList = ''
730
- if (reportsRow.length > 0) {
731
- let reportOptions = ''
732
- reportsRow.map((obj) => {
733
- reportOptions += `<option value="${obj.id}">${obj.title}</option>`
734
- })
735
- reportList += `<div class="row"><div class="form-group"><label class="control-label col-md-2">Report Name</label><div class="col-md-10"><select class="form-control" id="zreports_name" name="zreports_name">${reportOptions}</select></div></div></div>`
736
- reportList += `<br><button type="submit" class="btn btn-block btn-success">Submit </button> `
737
- reportList += `</form>`
738
- }
739
- row.reportList = reportList
740
-
741
- data.push(row)
742
- }
743
-
744
- return data
745
- }
746
-
747
- /*
748
- One line
749
- */
750
- zReport.filters2 = async (arr, companyId) => {
751
- console.log(arr)
752
- console.log(companyId)
753
- var html = ''
754
- let MYMODELS = myCache.get('MYMODELS')
755
- for (var i = 0; i < arr.length; i++) {
756
- var filter = arr[i]
757
-
758
- html += ` <div class="mb-3 row"><label for="${filter.name}"class="col-sm-4 col-form-label">${filter.label}</label><div class="col-sm-8">`
759
- if (filter.type == 1) {
760
- html += `<input type="text" class="datepicker form-control" name="${filter.name}" >`
761
- } else if (filter.type == 2) {
762
- let values = filter.value ? JSON.parse(filter.value) : []
763
- let options = `<option value="">Please Select</option>`
764
- if (values.length) {
765
- values.map(function (e) {
766
- options += `<option value="${e.key}">${e.label}</option>`
767
- })
768
- html += `<select class="form-control form-select" name="${filter.name}">${options}</select>`
769
- }
770
- } else if (filter.type == 3) {
771
- let values = filter.value || ''
772
- let explode = values.split('.')
773
- let table = explode[0]
774
- let options = `<option value="">Please Select</option>`
775
- if (table && Object.keys(MYMODELS[table]).length > 0) {
776
- let keycolumn = explode[1]
777
- let labelcolumn = explode[2]
778
- let MYMODEL = MYMODELS[table]
779
- let fields = MYMODEL.keys
780
- let where = ''
781
- if (fields.indexOf('company_id') > -1) {
782
- where = ` where company_id = ${companyId} `
783
- }
784
- let sql = `select * from "${table}" ${where} order by ${labelcolumn} ASC`
785
- let results = await connection.query(sql)
786
- for (const result of results) {
787
- options += `<option value="${result[keycolumn]}">${result[labelcolumn]}</option>`
788
- }
789
- }
790
- html += `<select class="form-control form-select" name="${filter.name}">${options}</select>`
791
- }
792
-
793
- html += `</div>
794
- </div>`
795
- }
796
-
797
- return html
798
- }
799
-
800
- zReport.filters = async (arr, companyId) => {
801
- var html = ''
802
-
803
- for (var i = 0; i < arr.length; i++) {
804
- var filter = arr[i]
805
-
806
- html += `<div class="mb-3"><label for="${filter.name}" class="form-label">${filter.label}</label>`
807
- if (filter.type == 1) {
808
- html += `<input type="text" class="datepicker form-control" name="${filter.name}" >`
809
- } else if (filter.type == 2) {
810
- let values = filter.value ? JSON.parse(filter.value) : []
811
- let options = ''
812
- if (values.length) {
813
- values.map(function (e) {
814
- options += `<option value="${e.key}">${e.label}</option>`
815
- })
816
- html += `<select class="form-control form-select" name="${filter.name}">${options}</select>`
817
- }
818
- } else if (filter.type == 3) {
819
- let values = filter.value || ''
820
- let explode = values.split('.')
821
- let table = explode[0]
822
- let options = ''
823
- if (table && fs.existsSync(dirRoot + '/models/' + table + '.js')) {
824
- let keycolumn = explode[1]
825
- let labelcolumn = explode[2]
826
- let MYMODEL = require('./../models/' + table)
827
- let fields = MYMODEL.keys
828
- let where = ''
829
- if (fields.indexOf('company_id') > -1) {
830
- where = ` where company_id = ${companyId} `
831
- }
832
- let results = await connection.query('select * from `' + table + '` ' + where + ' order by ' + labelcolumn + ' ASC')
833
- results.map(async (result) => {
834
- options += `<option value="${result[keycolumn]}">${result[labelcolumn]}</option>`
835
- })
836
- html += `<select class="form-control form-select" name="${filter.name}">${options}</select>`
837
- }
838
- }
839
-
840
- html += `</div>`
841
- }
842
-
843
- return html
844
- }
845
-
846
- zReport.showAll = async (req, res) => {
847
- var html = ''
848
- var companyId = res.locals.companyId
849
- var rows = await connection.query('select * from zreport where company_id = ? and parent_id IS NULL', [companyId])
850
- for (var i = 0; i < rows.length; i++) {
851
- var isEnd = false
852
-
853
- if (i == 0) html += `<div class="row">`
854
-
855
- if (i > 1) {
856
- if (i % 2 == 0) {
857
- html += `<div class="row">`
858
- isEnd = true
859
- }
860
-
861
- if (i == rows.length - 1) {
862
- isEnd = true
863
- }
864
- }
865
-
866
- var content = await zReport.show(rows[i].id, companyId)
867
- html += `<div class="col">${content}</div>`
868
-
869
- if (i == 1) html += `</div>`
870
-
871
- if (i > 1) {
872
- if (isEnd) {
873
- html += `</div>`
874
- isEnd = false
875
- }
876
- }
877
- }
878
-
879
- return html
880
- }
881
-
882
- zReport.show = async (id, companyId, csrfToken, isHideLink = false) => {
883
- var html = ''
884
- var result = await connection.result({
885
- table: 'zreport',
886
- where: {
887
- id: id,
888
- },
889
- })
890
- if (result.id) {
891
- if (result.parent_id) {
892
- result = await connection.result({
893
- table: 'zreport',
894
- where: {
895
- id: result.parent_id,
896
- },
897
- })
898
- }
899
-
900
- var arr = []
901
- arr.push({ id: result.id, label: result.title })
902
- var rows = await connection.results({
903
- table: 'zreport',
904
- where: {
905
- parent_id: result.id,
906
- },
907
- })
908
- rows.forEach(function (item) {
909
- arr.push({ id: item.id, label: item.title })
910
- })
911
-
912
- var reports = `<div class="mb-3 row"><label for="reporttype" class="col-sm-4 form-label">Report Name</label><div class="col-sm-8"><select class="form-control form-select" id="zreport_name" name="zreport_name">`
913
- arr.forEach(function (item) {
914
- reports += `<option value="${item.id}">${item.label}</option>`
915
- })
916
- reports += `</select></div></div>`
917
-
918
- var link = ''
919
- if (!isHideLink) {
920
- link = `<div class="card-footer">
921
- <i class="fa fa-copy copy-link" title="copy link" data-url="${CONFIG.app.url}/zreport/show/${result.id}/${result.title}"></i>
922
- <a href="${CONFIG.app.url}/zreport/show/${result.id}/${result.title}" class="card-link">${CONFIG.app.url}/zreport/show/${result.id}/${result.title}</a>
923
- </div>`
924
- }
925
- //one line filters2
926
- var filter = await zReport.filters2(result.filters || [], companyId)
927
- html += `<form class="form" method="post" action="/zreport/report" >`
928
- html += `<input type="hidden" name="_csrf" value="${csrfToken}">`
929
- html += `<div class="card boxy">
930
- <div class="card-body">
931
- <h2 class="card-title">${result.title}</h2>
932
- <p class="card-text">${result.description}</p><br>
933
-
934
- ${reports}
935
- ${filter}
936
- </div>
937
-
938
- <div class="card-body">
939
- <button type="submit" class="btn btn-success"><i class="fa fa-paper-plane"></i> Submit</button>
940
- </div>
941
-
942
- ${link}
943
-
944
- </div></form>`
945
- }
946
-
947
- return html
948
- }
949
-
950
- zReport.updateQuery = async (req, res) => {
951
- var body = req.body
952
- var orderby = body.orderby || ''
953
- var wheres = body.wheres || ''
954
- var scripts = body.scripts || ''
955
- var id = body.id
956
- console.log(req.body)
957
- var json = Util.jsonSuccess('Successfully save')
958
- var data = {}
959
- if (body.hasOwnProperty('orderby')) {
960
- data.orderby = orderby
961
- }
962
- if (body.hasOwnProperty('scripts')) {
963
- data.scripts = scripts
964
- }
965
- if (body.hasOwnProperty('wheres')) {
966
- data.wheres = wheres ? JSON.stringify(wheres) : null
967
- }
968
- try {
969
- await connection.update({
970
- table: 'zreport',
971
- data: data,
972
- where: {
973
- id: id,
974
- },
975
- })
976
- } catch (err) {
977
- json = Util.flashError('Error ' + err.toString())
978
- }
979
- res.json(json)
980
- }
981
-
982
- module.exports = zReport
1
+ const axios = require('axios')
2
+ const fs = require('fs-extra')
3
+ const nodemailer = require('nodemailer')
4
+ const qs = require('qs')
5
+ const moment = require('moment')
6
+ const XLSX = require('xlsx')
7
+ const Excel = require('exceljs')
8
+ const debug = require('./debug')
9
+ const zRoute = require('./zRoute')
10
+ const Util = require('./Util')
11
+ const connection = require('./connection')
12
+ const io = require('./io')
13
+ const config = require('dotenv').config()
14
+ const zReport = {}
15
+ const myCache = require('./cache')
16
+
17
+ /*
18
+ Custom Reports
19
+ Report Generator
20
+ */
21
+
22
+ /*
23
+ UI For edit excel File
24
+ */
25
+ zReport.reportData = (filename, data, sessions = {}) => {
26
+ console.log(filename)
27
+ const workbook = XLSX.readFile(filename)
28
+ const Sheets = workbook.Sheets
29
+ let sheet = Sheets[Object.keys(Sheets)[0]]
30
+ let excel = Util.excelSequence()
31
+ let ref = sheet['!ref']
32
+ let explode = ref.split(':')
33
+ let lastchar = explode[1]
34
+ let stringPattern = /[A-Z]+/i
35
+ let digitPattern = /[0-9]+/i
36
+ let stringColumn = lastchar.match(stringPattern)[0]
37
+ let maxColumn = excel.indexOf(stringColumn)
38
+
39
+ let maxRow = lastchar.match(digitPattern)[0]
40
+ //add 5 row for input cells
41
+ maxRow = parseInt(maxRow) + 5
42
+ let excelData = {}
43
+ let excelValue = {}
44
+ let tableModel = {}
45
+ let excelQuery = data.excel_query || {}
46
+ let dataForExcel = excelQuery.excel
47
+ let dataCallback = data.callback || []
48
+ let callback = {}
49
+ let objQuery = {}
50
+ let results = []
51
+
52
+ //set callback as object
53
+ dataCallback.map((m) => {
54
+ callback[m.name] = m.value
55
+ })
56
+
57
+ //console.log(JSON.stringify(sessions));
58
+
59
+ //create UI Button in excel cell
60
+ if (dataForExcel) {
61
+ dataForExcel.forEach((datafor, index) => {
62
+ let button = '',
63
+ name = datafor.name,
64
+ callback = datafor.callback || '',
65
+ value = datafor.value || ''
66
+ let split = value.split('.')
67
+ let len = split.length
68
+ let MYMODEL = {}
69
+ let table = '',
70
+ tableKey = ''
71
+ if (len == 4) {
72
+ table = split[2]
73
+ tableKey = split[3]
74
+ } else {
75
+ table = split[0]
76
+ tableKey = split[1]
77
+ }
78
+
79
+ const MYMODELS = zRoute.MYMODELS() || {}
80
+ MYMODEL = tableModel.hasOwnProperty(table) ? tableModel[table] : MYMODELS[table]
81
+ if (!objQuery[table]) {
82
+ objQuery[table] = []
83
+ }
84
+ objQuery[table].push(value)
85
+ if (!excelValue) {
86
+ excelValue[name] = ''
87
+ } else {
88
+ button = excelValue[name] || ''
89
+ }
90
+
91
+ let labels = value.indexOf('_SESSIONS_') > -1 ? value.replace('_SESSIONS_.', '') : tableKey
92
+ button += `<li class="dragged"> <button class="btn btn-info btn-excel" type="button" title="${name.replace('[', '').replace(']', '')} : ${value}">${MYMODEL.labels[labels]}
93
+ <input type="hidden" class="EXCEL" name="${name}" value='${value}_CALLBACK_SEPARATOR_${callback}'> <i class="fa fa-code call-me"></i> <i class="fa fa-trash trash-me"></i></button></li>`
94
+ excelValue[name] = button
95
+ })
96
+ }
97
+ //end UI
98
+
99
+ let table = `<table class="table table-striped table-bordered" >`
100
+ for (var x = 1; x < maxRow; x++) {
101
+ table += `<tr>`
102
+ let arr = []
103
+ for (var i = 0; i <= maxColumn; i++) {
104
+ let str = excel[i] + x
105
+ let strkey = excel[i] + '[' + x + ']'
106
+ let defaultValue = Object.prototype.hasOwnProperty.call(sheet, str) ? sheet[str].v : ''
107
+ let value = excelValue[strkey] || defaultValue
108
+ table += `<td width="80px" id="${str}" title="${str}" class="mydragable" data-col="${excel[i]}" data-row="${x}">${value}</td>`
109
+ arr.push(value)
110
+ }
111
+ table += `</tr>`
112
+ results.push(arr)
113
+ }
114
+ table += `</table>`
115
+ return {
116
+ table: table,
117
+ excelData: excelData,
118
+ results: results,
119
+ max: maxRow,
120
+ objQuery: objQuery,
121
+ }
122
+ }
123
+
124
+ //Save reports Setup
125
+ zReport.reportPostData = async (req, res) => {
126
+ let data_status = Util.jsonSuccess('Success')
127
+ try {
128
+ let body = req.body
129
+ ///console.log(body)
130
+ let post = zRoute.postData(body)
131
+ let ex = post.excel
132
+ delete post.excel
133
+ let id = body.id
134
+ let CALLBACK_SEPARATOR = '_CALLBACK_SEPARATOR_'
135
+
136
+ //remove CALLBACK in query
137
+ //sometimes bug in query callback separator in javascript
138
+ let query = body.query
139
+ query = Util.replaceAll(query, CALLBACK_SEPARATOR, '')
140
+ let callback = [],
141
+ excel = []
142
+ ex.map((e) => {
143
+ let name = e.name || ''
144
+ let value = e.value || ''
145
+ //console.log(value);
146
+ if (value.indexOf(CALLBACK_SEPARATOR) > -1) {
147
+ let split = value.split(CALLBACK_SEPARATOR)
148
+ excel.push({
149
+ name: name,
150
+ title: name.replace('[', '').replace(']', ''),
151
+ value: split[0],
152
+ callback: Util.replaceAll(split[1], "'", '"'),
153
+ })
154
+ }
155
+ })
156
+ post.excel = excel || ''
157
+ post.query = query || ''
158
+ await connection.update({
159
+ table: 'zreport',
160
+ data: { json: JSON.stringify(post) },
161
+ where: {
162
+ id: id,
163
+ },
164
+ })
165
+ } catch (err) {
166
+ debug(req, res, err)
167
+ data_status = Util.flashError(err.toString())
168
+ }
169
+ res.json(data_status)
170
+ }
171
+
172
+ //SUBMIT REPORT ROUTE FOR REPORT GENERATOR
173
+ zReport.submitReport = async (req, res) => {
174
+ let company_id = res.locals.companyId
175
+ let body = req.body
176
+ console.log('BODYYYYYY')
177
+ console.log(body)
178
+ delete body._csrf
179
+ const MYMODELS = zRoute.MYMODELS() || {}
180
+ //delete key where null
181
+ for (let keys in body) {
182
+ if (body[keys] == '') {
183
+ delete body[keys]
184
+ }
185
+ if (body[keys] == 'undefined') {
186
+ delete body[keys]
187
+ }
188
+ }
189
+ //console.log(body);
190
+ let $session = req.session.user
191
+ try {
192
+ let reportId = body.zreport_name
193
+ let row = await connection.result({
194
+ table: 'zreport',
195
+ where: {
196
+ id: reportId,
197
+ },
198
+ })
199
+ let json = row.json ? row.json : {}
200
+ console.log(json)
201
+ let query = json.query || ''
202
+ let table = json.tables[0] || ''
203
+ if (!table) {
204
+ return res.json('error not configure!')
205
+ }
206
+ let title = row.title
207
+ let jsonExcels = json.excel || []
208
+ let excelObj = {}
209
+ let callback = {}
210
+ jsonExcels.map((jsonExcel) => {
211
+ if (!excelObj[jsonExcel.name]) excelObj[jsonExcel.name] = []
212
+
213
+ let jsonvalue = Util.replaceAll(jsonExcel.value, '.', '___')
214
+ let nameCallback = jsonExcel.title + '___' + jsonvalue
215
+
216
+ jsonExcel.callbackName = nameCallback
217
+
218
+ let split = jsonvalue.split('___')
219
+ if (split.length == 2) {
220
+ jsonExcel.data = jsonvalue
221
+ } else if (split.length == 3) {
222
+ jsonExcel.data = split[0] + '___' + split[1]
223
+ } else {
224
+ jsonExcel.data = jsonvalue
225
+ }
226
+
227
+ if (jsonExcel.callback) callback[nameCallback] = jsonExcel.callback
228
+
229
+ excelObj[jsonExcel.name].push(jsonExcel)
230
+ })
231
+
232
+ delete body.zreports_name
233
+
234
+ //console.log(filterWhere);
235
+ //excel instance
236
+ var workbook = new Excel.Workbook()
237
+ let filename = `${dirRoot}/public/uploads/zreport/${row.excel}`
238
+ await workbook.xlsx.readFile(filename)
239
+ var worksheet = workbook.worksheets[0] //the first one;
240
+ let images = [] // store images
241
+ // Force workbook calculation on load
242
+ //workbook.calcProperties.fullCalcOnLoad = true;
243
+
244
+ //set images
245
+ var setExcelImages = (script, data, r, c) => {
246
+ let excelsq = Util.excelSequence()
247
+ //A1 = col=0 row=0
248
+ //convert column to integer
249
+ c = excelsq.indexOf(c)
250
+ r = r - 1
251
+ script = script || ''
252
+ if (script == '') {
253
+ return ''
254
+ }
255
+ data = data || ''
256
+ if (data == '') {
257
+ return ''
258
+ }
259
+
260
+ let str = script.replace('hasImages(', '').replace(')', '')
261
+ let explode = str.split(',')
262
+ let table = explode[0].trim() || ''
263
+ let width = parseInt(explode[1]) || 200
264
+ let height = parseInt(explode[2]) || 200
265
+ let photoName = `${dirRoot}/public/uploads/${table}/${data}`
266
+ if (!fs.existsSync(photoName)) {
267
+ return ''
268
+ }
269
+ let image = workbook.addImage({
270
+ filename: photoName,
271
+ extension: data.split('.').pop(),
272
+ })
273
+ images.push({
274
+ image: image,
275
+ width: width,
276
+ height: height,
277
+ c: c,
278
+ r: r,
279
+ })
280
+
281
+ return ''
282
+ }
283
+ //end set images
284
+
285
+ //query where sql;
286
+ //query executes
287
+ console.log('query')
288
+ console.log(JSON.stringify(query))
289
+ console.log('query')
290
+
291
+ let datas = []
292
+ if (query) {
293
+ var wheres = []
294
+ var whereArr = []
295
+ var where = ''
296
+ //TODO if has order by limit
297
+ var sql = json.query
298
+ console.log('sql')
299
+ console.log(sql)
300
+ console.log('sql')
301
+
302
+ var num = 1
303
+ for (var key in json.filter) {
304
+ if (body[key]) {
305
+ wheres.push(Util.replaceAll(json.filter[key], '?', `$${num}`))
306
+ if (json.filter[key].includes('LIKE')) {
307
+ whereArr.push(`%${body[key]}%`)
308
+ } else {
309
+ whereArr.push(body[key])
310
+ }
311
+ num++
312
+ }
313
+ }
314
+
315
+ //row.wheres from database
316
+ var rowWheres = row.wheres || []
317
+ rowWheres.forEach(function (item) {
318
+ wheres.push(item)
319
+ })
320
+
321
+ if (wheres.length) {
322
+ where += ' WHERE '
323
+ where += wheres.join(' AND ')
324
+ }
325
+ var orderby = ''
326
+ if (row.orderby) {
327
+ orderby = ` ORDER BY ${row.orderby} `
328
+ }
329
+ sql = sql + where + orderby
330
+ console.log('sql')
331
+ console.log(sql)
332
+ console.log('sql')
333
+
334
+ console.log('whereArr')
335
+ console.log(JSON.stringify(whereArr))
336
+ console.log('whereArr')
337
+ datas = await connection.query(sql, whereArr)
338
+ }
339
+
340
+ let excelMap = {}
341
+ let beginLooping = 0
342
+ for (let keys in excelObj) {
343
+ let split = keys.split('[')
344
+ let column = split[0]
345
+ let idx = parseInt(split[1])
346
+
347
+ if (!excelMap[keys]) excelMap[keys] = {}
348
+
349
+ excelMap[keys].column = column
350
+ excelMap[keys].row = idx
351
+ excelMap[keys].rowColumn = column + idx
352
+ if (beginLooping == 0) beginLooping = idx
353
+ }
354
+
355
+ console.log('datas')
356
+ console.log(JSON.stringify(datas))
357
+ console.log('datas')
358
+
359
+ if (datas.length > 1) {
360
+ try {
361
+ for (var i = 0; i < datas.length - 1; i++) {
362
+ let r = i + beginLooping
363
+ worksheet.spliceRows(r + 1, 1, true)
364
+ worksheet.duplicateRow(r, 1, true)
365
+ }
366
+ } catch (e) {
367
+ console.log('no style')
368
+ }
369
+ }
370
+
371
+ var ioroom = function (err) {
372
+ io.to(res.locals.user.token).emit('error', err.toString())
373
+ }
374
+
375
+ datas.forEach((data, index) => {
376
+ for (let keys in excelObj) {
377
+ let columnIndex = excelMap[keys].column
378
+ let rowIndex = parseInt(excelMap[keys].row) + index
379
+ let rowColumn = columnIndex + rowIndex
380
+ let value = excelObj[keys]
381
+ .map((m) => {
382
+ //console.log(m)
383
+ return callback.hasOwnProperty(m.callbackName) ? (callback[m.callbackName].indexOf('hasImages') > -1 ? setExcelImages(callback[m.callbackName], data[m.data], rowIndex, columnIndex) : zReport.callbackReport(callback[m.callbackName], data[m.data], rowIndex, columnIndex, null, data, ioroom, res)) : data[m.data]
384
+ })
385
+ .join(' ')
386
+
387
+ worksheet.getCell(rowColumn).value = Util.isNumeric(value) ? parseFloat(value) : value
388
+ }
389
+ })
390
+ //build images if any
391
+ images.map((m) => {
392
+ worksheet.addImage(m.image, {
393
+ tl: { col: m.c, row: m.r },
394
+ ext: { width: m.width, height: m.height },
395
+ })
396
+ })
397
+
398
+ var fileName = title + '.xlsx'
399
+ if (row.scripts) {
400
+ return zReport.additionalScripts(req, res, row.scripts, fileName, datas, moment, Util, dirRoot, ioroom, connection, worksheet, workbook, debug)
401
+ } else {
402
+ res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
403
+ res.setHeader('Content-Disposition', 'attachment; filename=' + fileName)
404
+ await workbook.xlsx.write(res)
405
+ res.end()
406
+ }
407
+ } catch (e) {
408
+ console.log(e)
409
+ debug(req, res, e)
410
+ res.json(e.toString())
411
+ }
412
+ }
413
+
414
+ zReport.additionalScripts = (req, res, script, fileName, datas, moment, Util, dirRoot, ioroom, connection, worksheet, workbook) => {
415
+ script = script.trim() || ''
416
+ let CALL = {
417
+ Util: Util,
418
+ moment: moment,
419
+ datas: datas,
420
+ dirRoot: dirRoot,
421
+ ioroom: ioroom,
422
+ res: res,
423
+ req: req,
424
+ fileName: fileName,
425
+ connection: connection,
426
+ worksheet: worksheet,
427
+ workbook: workbook,
428
+ }
429
+ let STRINGS_VARIABLE = ''
430
+ Object.keys(CALL).forEach(function (item) {
431
+ STRINGS_VARIABLE += ` var ${item} = this.${item}; `
432
+ })
433
+ return Function(`
434
+ var runcode = async() => {
435
+ ${STRINGS_VARIABLE}
436
+ try {
437
+ ${script};
438
+ res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
439
+ res.setHeader('Content-Disposition', 'attachment; filename=' + fileName);
440
+ await workbook.xlsx.write(res);
441
+ res.end();
442
+ } catch(e) {
443
+ console.log(e.toString());
444
+ res.json(e.toString());
445
+ }
446
+ };
447
+ runcode();
448
+
449
+ `).call(CALL)
450
+ }
451
+
452
+ zReport.callbackReport = (script, data, row, column, selectFromTable, obj, ioroom, res) => {
453
+ script = script.trim() || ''
454
+ let results = {}
455
+ for (var key in obj) {
456
+ let name = key.split('___')
457
+ if (name.length > 0) {
458
+ results[name[1]] = obj[key]
459
+ }
460
+ }
461
+
462
+ let first = script.charAt(0)
463
+ let CALL = {
464
+ Util: Util,
465
+ obj: obj,
466
+ moment: moment,
467
+ data: data,
468
+ dirRoot: dirRoot,
469
+ selectFromTable: selectFromTable,
470
+ row: row,
471
+ col: column,
472
+ results: results,
473
+ ioroom,
474
+ res: res,
475
+ }
476
+ let STRINGS_VARIABLE = ''
477
+ Object.keys(CALL).forEach(function (item) {
478
+ STRINGS_VARIABLE += ` var ${item} = this.${item}; `
479
+ })
480
+ return Function(`
481
+ ${STRINGS_VARIABLE}
482
+ try {
483
+ ${script};
484
+ } catch(e) {
485
+ console.log(e);
486
+ ioroom("CELLS : " + col+row+ " " +e.toString());
487
+ }
488
+ `).call(CALL)
489
+ }
490
+
491
+ zReport.staticDataReport = (data, filter) => {
492
+ delete data.id
493
+ delete data.password
494
+ delete data.token
495
+ let session_obj = {}
496
+ let session_obj_fields = {}
497
+
498
+ filter.map((m) => {
499
+ let keyName = m.name
500
+ session_obj[keyName] = m.label
501
+ session_obj_fields[keyName] = {
502
+ name: keyName,
503
+ title: m.label,
504
+ type: 'text',
505
+ category: 'text',
506
+ length: 'undefined',
507
+ required: false,
508
+ search: 'LIKE',
509
+ key: '',
510
+ placeholder: m.label,
511
+ }
512
+ })
513
+
514
+ for (var key in data) {
515
+ let keyName = '$session.' + key
516
+ session_obj[keyName] = key
517
+ session_obj_fields[keyName] = {
518
+ name: keyName,
519
+ title: key,
520
+ type: 'text',
521
+ category: 'text',
522
+ length: 'undefined',
523
+ required: false,
524
+ search: 'LIKE',
525
+ key: '',
526
+ placeholder: key,
527
+ }
528
+
529
+ if (key == 'company') {
530
+ for (var k in data[key]) {
531
+ let keyName2 = '$session.company.' + k
532
+ session_obj[keyName2] = k
533
+ session_obj_fields[keyName2] = {
534
+ name: keyName2,
535
+ title: key + ' ' + k,
536
+ type: 'text',
537
+ category: 'text',
538
+ length: 'undefined',
539
+ required: false,
540
+ search: 'LIKE',
541
+ key: '',
542
+ placeholder: key + ' ' + k,
543
+ }
544
+ }
545
+ }
546
+ }
547
+
548
+ return {
549
+ table: '_SESSIONS_',
550
+ routeName: '_SESSIONS_',
551
+ keys: Object.keys(session_obj),
552
+ labels: session_obj,
553
+ fields: session_obj_fields,
554
+ relations: {},
555
+ dropdowns: {},
556
+ modules: {},
557
+ }
558
+ }
559
+
560
+ zReport.dataHTMLList = async () => {
561
+ let html = ''
562
+ let results = await connection.results({
563
+ table: 'zreports',
564
+ whereArray: [{ field: 'parent_id', value: 'IS NULL', type: 'inline' }],
565
+ order_by: ['id desc'],
566
+ })
567
+ for (const result of results) {
568
+ let myfilter = await zReport.filters2(result.filter, result.company_id)
569
+ let childReport = ''
570
+ let rows = await connection.results({
571
+ table: 'zreports',
572
+ where: {
573
+ parent_id: result.id,
574
+ },
575
+ })
576
+ rows.forEach((row, index) => {
577
+ let action = ''
578
+ if (row.excel) {
579
+ action += `<button title="setup Report" onclick="location.href='/zreports/setup/${row.id}'" data-id="${row.id}" class="btn image-button boxy-small" type="button"><img src="/assets/icons/settings-filled.svg" class="icons-bg-black"></button>`
580
+ }
581
+ action += `<button title="edit Report" onclick="location.href='/zreports/update/${row.id}'" class="btn image-button boxy-small" type="button"><img src="/assets/icons/pencil.svg" class="icons-bg-black"></button>`
582
+ action += `<button title="delete Report" onclick="deletereport('${row.id}')" data-id="${row.id}" class="btn image-button boxy-small" type="button"><img src="/assets/icons/trash.svg" class="icons-bg-black"></button>`
583
+ childReport += `<tr><td>${index + 1}. </td><td>${row.title}</td><td><a href="/uploads/zreports/${row.excel}">Excel File</a></td><td>${action}</td></tr>`
584
+ })
585
+
586
+ html += `<div class="col-xs-12 col-md-6">
587
+ <div class="boxy bottom-down">
588
+ <div class="float">
589
+ <span class="icon-small icons-primary" onclick="location.href='/zreports/update/${result.id}'" title="${LANGUAGE.update}"><img class="icons-bg-white gridupdate icon-image" src="/assets/icons/edit.svg"></span>
590
+
591
+ <span class="icon-small icons-danger float-end" onclick="deletereport('${result.id}')" title="${LANGUAGE.delete}"><img class="icons-bg-white griddelete icon-image" src="/assets/icons/trash-filled.svg"></span>
592
+ </div>
593
+
594
+ <div class="container-fluid">
595
+ <h2 class="text text-center text-capitalize">Custom Reports: ${result.title}</h2>
596
+ <p class="card-text card-text text-center text-muted">${result.description}</p>
597
+ <div class="card">
598
+ <div class="card-body">
599
+ <a class="card-link" href="${process.env.APP_URL}/zreports/show/${result.id}" target="_blank">${process.env.APP_URL}/zreports/show/${result.id}</a>
600
+
601
+ <div class="float-end">
602
+ <img src="/assets/icons/copy.svg" class="icons-bg-black cursor" onclick="copyToClipboard('${process.env.APP_URL}/zreports/show/${result.id}')" alt="Copy"></div>
603
+ </div>
604
+ </div>
605
+
606
+ <hr>
607
+ <form method="post" action="/zreports">${myfilter}
608
+ <button type="submit" class="btn btn-block btn-success mt-3">Submit </button>
609
+ </form>
610
+ <hr>
611
+ <div class="form-group divReportList">
612
+ <div class="panel panel-default">
613
+ <div class="panel-heading">Report List</div>
614
+ <div class="panel-body">
615
+ <table class="table table-hover">
616
+ <thead>
617
+ <tr>
618
+ <th width="3%">#</th>
619
+ <th width="50%">Name</th>
620
+ <th>File</th>
621
+ <th> <button title="add Report" data-id="${result.id}" onclick="$('#parent_id').val(${result.id})" data-bs-toggle="modal" data-bs-target="#reportModal" class="btn image-button boxy-small float-end" type="button"><img src="/assets/icons/plus.svg" class="icons-bg-black"></button></th>
622
+ </tr>
623
+ </thead>
624
+ <tbody class="body-reportList" data-value="">${childReport}</tbody>
625
+ </table>
626
+ </div>
627
+ </div>
628
+ </div>
629
+
630
+ </div>
631
+ </div>
632
+ </div>`
633
+ }
634
+ return html
635
+ }
636
+
637
+ zReport.listReports = async (data) => {
638
+ let html = ''
639
+ let rows = await connection.query('select * from zreport where parent_id = ?', [data.id])
640
+ rows.forEach((obj, index) => {
641
+ let action = ''
642
+ if (obj.excel) {
643
+ action += `<a href="/zreport/setup/${obj.id}" class="btn btn-default"> <i class="fa fa-gear" title="Setup Report"></i> </a>`
644
+ }
645
+ action += `<a href="/zreport/update/${obj.id}" class="btn btn-default"><i class="fa fa-pencil" title="Edit Data"></i> </a>`
646
+ action += `<a href="#" class="btn btn-default reportdelete" data-id="${obj.id}"><i class="fa fa-trash " title="Delete Report"></i></a>`
647
+ html += `<tr><td>${index + 1}. </td><td>${obj.title}</td><td><a href="/uploads/zreport/${obj.excel}">Excel File</a></td><td>${action}</td></tr>`
648
+ })
649
+
650
+ return `<div class="form-group divReportList">
651
+ <div class="panel panel-default">
652
+ <div class="panel-heading">Report List</div>
653
+ <div class="panel-body">
654
+ <table class="table table-hover">
655
+ <thead>
656
+ <tr>
657
+ <th width="3%">#</th>
658
+ <th width="50%">Name</th>
659
+ <th>File</th>
660
+ <th class="pull-right"><a href="/zreport/create?id=${data.id}" class="btn btn-default" title="Add" id="addReportList"><i class="fa fa-plus"></i></a></th>
661
+ </tr>
662
+ </thead>
663
+ <tbody class="body-reportList" data-value="">${html}</tbody>
664
+ </table>
665
+ </div>
666
+ </div>
667
+ </div>`
668
+ }
669
+
670
+ zReport.filterReport = async (req, res) => {
671
+ let id = req.params.id || ''
672
+ let companyId = res.locals.companyId
673
+ let data = [],
674
+ rows = []
675
+ if (id == '') {
676
+ rows = await connection.query('select * from zreports where company_id = ? and parent_id IS NULL', [companyId])
677
+ } else {
678
+ rows = await connection.results({ table: 'zreports', where: { id: id, company_id: companyId } })
679
+ }
680
+
681
+ console.log(rows)
682
+ for (var i = 0; i < rows.length; i++) {
683
+ var row = rows[i]
684
+ //rows.forEach(async function (row) {
685
+ let reportsRow = await connection.results({ table: 'zreports', where: { parent_id: row.id } })
686
+ console.log(reportsRow)
687
+ let filterHtml = reportsRow.length > 0 ? `<form method="post" action="/zreport" >` : ''
688
+ let filters = JSON.parse(row.filter) || []
689
+ console.log('filters :')
690
+ console.log(filters)
691
+ row.listReports = await zReport.listReports(row)
692
+ for (var x = 0; x < filters.length; x++) {
693
+ let filter = filters[x]
694
+ let options = `<option value="">Please Select</option>`
695
+ let html = ''
696
+ if (filter.type == 1) {
697
+ html = `<input type="text" class="datepicker form-control" name="${filter.name}" >`
698
+ } else if (filter.type == 2) {
699
+ let values = JSON.parse(filter.value) || []
700
+ values.map(function (e) {
701
+ options += `<option value="${e.key}">${e.label}</option>`
702
+ })
703
+ html = `<select class="form-control form-select" name="${filter.name}">${options}</select>`
704
+ } else if (filter.type == 3) {
705
+ let values = filter.value || ''
706
+ let explode = values.split('.')
707
+ let table = explode[0]
708
+ if (table && fs.existsSync(dirRoot + '/models/' + table + '.js')) {
709
+ let keycolumn = explode[1]
710
+ let labelcolumn = explode[2]
711
+ let MYMODEL = require('./../models/' + table)
712
+ let fields = MYMODEL.keys
713
+ let where = ''
714
+ if (fields.indexOf('company_id') > -1) {
715
+ where = ` where company_id = ${company_id} `
716
+ }
717
+ let results = await connection.query('select * from `' + table + '` ' + where + ' order by ' + labelcolumn + ' ASC')
718
+ results.map(async (result) => {
719
+ options += `<option value="${result[keycolumn]}">${result[labelcolumn]}</option>`
720
+ })
721
+ html = `<select class="form-control form-select" name="${filter.name}">${options}</select>`
722
+ }
723
+ }
724
+ filterHtml += `<div class="row"><div class="form-group"><label class="control-label col-md-2">${filter.label}</label><div class="col-md-10">${html}</div></div></div><br>`
725
+ row.filterHtml = filterHtml
726
+ }
727
+
728
+ //drop down report name
729
+ let reportList = ''
730
+ if (reportsRow.length > 0) {
731
+ let reportOptions = ''
732
+ reportsRow.map((obj) => {
733
+ reportOptions += `<option value="${obj.id}">${obj.title}</option>`
734
+ })
735
+ reportList += `<div class="row"><div class="form-group"><label class="control-label col-md-2">Report Name</label><div class="col-md-10"><select class="form-control" id="zreports_name" name="zreports_name">${reportOptions}</select></div></div></div>`
736
+ reportList += `<br><button type="submit" class="btn btn-block btn-success">Submit </button> `
737
+ reportList += `</form>`
738
+ }
739
+ row.reportList = reportList
740
+
741
+ data.push(row)
742
+ }
743
+
744
+ return data
745
+ }
746
+
747
+ /*
748
+ One line
749
+ */
750
+ zReport.filters2 = async (arr, companyId) => {
751
+ console.log(arr)
752
+ console.log(companyId)
753
+ var html = ''
754
+ let MYMODELS = myCache.get('MYMODELS')
755
+ for (var i = 0; i < arr.length; i++) {
756
+ var filter = arr[i]
757
+
758
+ html += ` <div class="mb-3 row"><label for="${filter.name}"class="col-sm-4 col-form-label">${filter.label}</label><div class="col-sm-8">`
759
+ if (filter.type == 1) {
760
+ html += `<input type="text" class="datepicker form-control" name="${filter.name}" >`
761
+ } else if (filter.type == 2) {
762
+ let values = filter.value ? JSON.parse(filter.value) : []
763
+ let options = `<option value="">Please Select</option>`
764
+ if (values.length) {
765
+ values.map(function (e) {
766
+ options += `<option value="${e.key}">${e.label}</option>`
767
+ })
768
+ html += `<select class="form-control form-select" name="${filter.name}">${options}</select>`
769
+ }
770
+ } else if (filter.type == 3) {
771
+ let values = filter.value || ''
772
+ let explode = values.split('.')
773
+ let table = explode[0]
774
+ let options = `<option value="">Please Select</option>`
775
+ if (table && Object.keys(MYMODELS[table]).length > 0) {
776
+ let keycolumn = explode[1]
777
+ let labelcolumn = explode[2]
778
+ let MYMODEL = MYMODELS[table]
779
+ let fields = MYMODEL.keys
780
+ let where = ''
781
+ if (fields.indexOf('company_id') > -1) {
782
+ where = ` where company_id = ${companyId} `
783
+ }
784
+ let sql = `select * from "${table}" ${where} order by ${labelcolumn} ASC`
785
+ let results = await connection.query(sql)
786
+ for (const result of results) {
787
+ options += `<option value="${result[keycolumn]}">${result[labelcolumn]}</option>`
788
+ }
789
+ }
790
+ html += `<select class="form-control form-select" name="${filter.name}">${options}</select>`
791
+ }
792
+
793
+ html += `</div>
794
+ </div>`
795
+ }
796
+
797
+ return html
798
+ }
799
+
800
+ zReport.filters = async (arr, companyId) => {
801
+ var html = ''
802
+
803
+ for (var i = 0; i < arr.length; i++) {
804
+ var filter = arr[i]
805
+
806
+ html += `<div class="mb-3"><label for="${filter.name}" class="form-label">${filter.label}</label>`
807
+ if (filter.type == 1) {
808
+ html += `<input type="text" class="datepicker form-control" name="${filter.name}" >`
809
+ } else if (filter.type == 2) {
810
+ let values = filter.value ? JSON.parse(filter.value) : []
811
+ let options = ''
812
+ if (values.length) {
813
+ values.map(function (e) {
814
+ options += `<option value="${e.key}">${e.label}</option>`
815
+ })
816
+ html += `<select class="form-control form-select" name="${filter.name}">${options}</select>`
817
+ }
818
+ } else if (filter.type == 3) {
819
+ let values = filter.value || ''
820
+ let explode = values.split('.')
821
+ let table = explode[0]
822
+ let options = ''
823
+ if (table && fs.existsSync(dirRoot + '/models/' + table + '.js')) {
824
+ let keycolumn = explode[1]
825
+ let labelcolumn = explode[2]
826
+ let MYMODEL = require('./../models/' + table)
827
+ let fields = MYMODEL.keys
828
+ let where = ''
829
+ if (fields.indexOf('company_id') > -1) {
830
+ where = ` where company_id = ${companyId} `
831
+ }
832
+ let results = await connection.query('select * from `' + table + '` ' + where + ' order by ' + labelcolumn + ' ASC')
833
+ results.map(async (result) => {
834
+ options += `<option value="${result[keycolumn]}">${result[labelcolumn]}</option>`
835
+ })
836
+ html += `<select class="form-control form-select" name="${filter.name}">${options}</select>`
837
+ }
838
+ }
839
+
840
+ html += `</div>`
841
+ }
842
+
843
+ return html
844
+ }
845
+
846
+ zReport.showAll = async (req, res) => {
847
+ var html = ''
848
+ var companyId = res.locals.companyId
849
+ var rows = await connection.query('select * from zreport where company_id = ? and parent_id IS NULL', [companyId])
850
+ for (var i = 0; i < rows.length; i++) {
851
+ var isEnd = false
852
+
853
+ if (i == 0) html += `<div class="row">`
854
+
855
+ if (i > 1) {
856
+ if (i % 2 == 0) {
857
+ html += `<div class="row">`
858
+ isEnd = true
859
+ }
860
+
861
+ if (i == rows.length - 1) {
862
+ isEnd = true
863
+ }
864
+ }
865
+
866
+ var content = await zReport.show(rows[i].id, companyId)
867
+ html += `<div class="col">${content}</div>`
868
+
869
+ if (i == 1) html += `</div>`
870
+
871
+ if (i > 1) {
872
+ if (isEnd) {
873
+ html += `</div>`
874
+ isEnd = false
875
+ }
876
+ }
877
+ }
878
+
879
+ return html
880
+ }
881
+
882
+ zReport.show = async (id, companyId, csrfToken, isHideLink = false) => {
883
+ var html = ''
884
+ var result = await connection.result({
885
+ table: 'zreport',
886
+ where: {
887
+ id: id,
888
+ },
889
+ })
890
+ if (result.id) {
891
+ if (result.parent_id) {
892
+ result = await connection.result({
893
+ table: 'zreport',
894
+ where: {
895
+ id: result.parent_id,
896
+ },
897
+ })
898
+ }
899
+
900
+ var arr = []
901
+ arr.push({ id: result.id, label: result.title })
902
+ var rows = await connection.results({
903
+ table: 'zreport',
904
+ where: {
905
+ parent_id: result.id,
906
+ },
907
+ })
908
+ rows.forEach(function (item) {
909
+ arr.push({ id: item.id, label: item.title })
910
+ })
911
+
912
+ var reports = `<div class="mb-3 row"><label for="reporttype" class="col-sm-4 form-label">Report Name</label><div class="col-sm-8"><select class="form-control form-select" id="zreport_name" name="zreport_name">`
913
+ arr.forEach(function (item) {
914
+ reports += `<option value="${item.id}">${item.label}</option>`
915
+ })
916
+ reports += `</select></div></div>`
917
+
918
+ var link = ''
919
+ if (!isHideLink) {
920
+ link = `<div class="card-footer">
921
+ <i class="fa fa-copy copy-link" title="copy link" data-url="${CONFIG.app.url}/zreport/show/${result.id}/${result.title}"></i>
922
+ <a href="${CONFIG.app.url}/zreport/show/${result.id}/${result.title}" class="card-link">${CONFIG.app.url}/zreport/show/${result.id}/${result.title}</a>
923
+ </div>`
924
+ }
925
+ //one line filters2
926
+ var filter = await zReport.filters2(result.filters || [], companyId)
927
+ html += `<form class="form" method="post" action="/zreport/report" >`
928
+ html += `<input type="hidden" name="_csrf" value="${csrfToken}">`
929
+ html += `<div class="card boxy">
930
+ <div class="card-body">
931
+ <h2 class="card-title">${result.title}</h2>
932
+ <p class="card-text">${result.description}</p><br>
933
+
934
+ ${reports}
935
+ ${filter}
936
+ </div>
937
+
938
+ <div class="card-body">
939
+ <button type="submit" class="btn btn-success"><i class="fa fa-paper-plane"></i> Submit</button>
940
+ </div>
941
+
942
+ ${link}
943
+
944
+ </div></form>`
945
+ }
946
+
947
+ return html
948
+ }
949
+
950
+ zReport.updateQuery = async (req, res) => {
951
+ var body = req.body
952
+ var orderby = body.orderby || ''
953
+ var wheres = body.wheres || ''
954
+ var scripts = body.scripts || ''
955
+ var id = body.id
956
+ console.log(req.body)
957
+ var json = Util.jsonSuccess('Successfully save')
958
+ var data = {}
959
+ if (body.hasOwnProperty('orderby')) {
960
+ data.orderby = orderby
961
+ }
962
+ if (body.hasOwnProperty('scripts')) {
963
+ data.scripts = scripts
964
+ }
965
+ if (body.hasOwnProperty('wheres')) {
966
+ data.wheres = wheres ? JSON.stringify(wheres) : null
967
+ }
968
+ try {
969
+ await connection.update({
970
+ table: 'zreport',
971
+ data: data,
972
+ where: {
973
+ id: id,
974
+ },
975
+ })
976
+ } catch (err) {
977
+ json = Util.flashError('Error ' + err.toString())
978
+ }
979
+ res.json(json)
980
+ }
981
+
982
+ module.exports = zReport