@agung_dhewe/webapps 1.1.2

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 (130) hide show
  1. package/LICENSE +28 -0
  2. package/README.md +2 -0
  3. package/jsconfig.json +10 -0
  4. package/libs/fgta5js-dist/fgta5js-v1.8.3.min.css +2 -0
  5. package/libs/fgta5js-dist/fgta5js-v1.8.3.min.js +11 -0
  6. package/libs/fgta5js-dist/fgta5js-v1.8.3.min.js.map +1 -0
  7. package/libs/fgta5js-dist/fonts/karla-italic-latin-ext.woff2 +0 -0
  8. package/libs/fgta5js-dist/fonts/karla-italic-latin.woff2 +0 -0
  9. package/libs/fgta5js-dist/fonts/karla-normal-latin-ext.woff2 +0 -0
  10. package/libs/fgta5js-dist/fonts/karla-normal-latin.woff2 +0 -0
  11. package/libs/fgta5js-dist/fonts/karla.css +142 -0
  12. package/libs/webmodule/module-edit.css +163 -0
  13. package/libs/webmodule/module-footer.css +22 -0
  14. package/libs/webmodule/module-list.css +25 -0
  15. package/libs/webmodule/module.css +52 -0
  16. package/libs/webmodule/module.js +195 -0
  17. package/libs/webmodule/pagehelper.mjs +45 -0
  18. package/modules/generator/appgen-components.mjs +142 -0
  19. package/modules/generator/appgen-icons.mjs +6 -0
  20. package/modules/generator/appgen-io.mjs +784 -0
  21. package/modules/generator/appgen-ui-search.mjs +173 -0
  22. package/modules/generator/appgen-ui-unique.mjs +153 -0
  23. package/modules/generator/appgen-ui.mjs +1181 -0
  24. package/modules/generator/generator-context.mjs +18 -0
  25. package/modules/generator/generator-designtemplate.html +1508 -0
  26. package/modules/generator/generator-ext.html +0 -0
  27. package/modules/generator/generator-ext.mjs +3 -0
  28. package/modules/generator/generator.css +642 -0
  29. package/modules/generator/generator.mjs +195 -0
  30. package/modules/generator/generator.png +0 -0
  31. package/modules/generator/generatorEdit.html +185 -0
  32. package/modules/generator/generatorEdit.mjs +238 -0
  33. package/modules/generator/generatorList.html +32 -0
  34. package/modules/generator/generatorList.mjs +243 -0
  35. package/modules/login/login.css +11 -0
  36. package/modules/login/login.html +12 -0
  37. package/modules/login/login.mjs +111 -0
  38. package/package.json +46 -0
  39. package/percobaan/simmpan-ke-minio.js +24 -0
  40. package/src/api.js +80 -0
  41. package/src/apis/generator.api.js +226 -0
  42. package/src/apis/login.api.js +109 -0
  43. package/src/bucket.js +24 -0
  44. package/src/context.js +26 -0
  45. package/src/datalog.sql +22 -0
  46. package/src/datarecords.js +0 -0
  47. package/src/db.js +61 -0
  48. package/src/generator/createApiExtenderModule.js +54 -0
  49. package/src/generator/createApiModule.js +218 -0
  50. package/src/generator/createIcon.js +62 -0
  51. package/src/generator/createInfoAboutExtender.js +42 -0
  52. package/src/generator/createInfoLogs.js +41 -0
  53. package/src/generator/createInfoRecordExtender.js +41 -0
  54. package/src/generator/createModuleContext.js +48 -0
  55. package/src/generator/createModuleDetilEditHtml.js +110 -0
  56. package/src/generator/createModuleDetilEditMjs.js +172 -0
  57. package/src/generator/createModuleDetilListHtml.js +146 -0
  58. package/src/generator/createModuleDetilListMjs.js +73 -0
  59. package/src/generator/createModuleEjs.js +51 -0
  60. package/src/generator/createModuleExtenderHtml.js +43 -0
  61. package/src/generator/createModuleExtenderMjs.js +43 -0
  62. package/src/generator/createModuleHeaderEditHtml.js +148 -0
  63. package/src/generator/createModuleHeaderEditMjs.js +197 -0
  64. package/src/generator/createModuleHeaderListHtml.js +144 -0
  65. package/src/generator/createModuleHeaderListMjs.js +67 -0
  66. package/src/generator/createModuleMjs.js +67 -0
  67. package/src/generator/createModuleRollup.js +42 -0
  68. package/src/generator/createProgramData.js +96 -0
  69. package/src/generator/createTable.js +156 -0
  70. package/src/generator/ddl.js +475 -0
  71. package/src/generator/helper.js +149 -0
  72. package/src/generator/templates/__rollup-module.ejs +90 -0
  73. package/src/generator/templates/api-extender-module.js.ejs +0 -0
  74. package/src/generator/templates/api-module.js.ejs +818 -0
  75. package/src/generator/templates/module-context.ejs +16 -0
  76. package/src/generator/templates/module-ext-about.ejs +1 -0
  77. package/src/generator/templates/module-ext-record.ejs +1 -0
  78. package/src/generator/templates/module-ext.html.ejs +3 -0
  79. package/src/generator/templates/module-ext.mjs.ejs +21 -0
  80. package/src/generator/templates/module-logs.ejs +14 -0
  81. package/src/generator/templates/module.ejs.ejs +48 -0
  82. package/src/generator/templates/module.mjs.ejs +256 -0
  83. package/src/generator/templates/moduleDetilEdit.html.ejs +34 -0
  84. package/src/generator/templates/moduleDetilEdit.mjs.ejs +792 -0
  85. package/src/generator/templates/moduleDetilList.html.ejs +26 -0
  86. package/src/generator/templates/moduleDetilList.mjs.ejs +319 -0
  87. package/src/generator/templates/moduleHeaderEdit.html.ejs +53 -0
  88. package/src/generator/templates/moduleHeaderEdit.mjs.ejs +807 -0
  89. package/src/generator/templates/moduleHeaderList.html.ejs +24 -0
  90. package/src/generator/templates/moduleHeaderList.mjs.ejs +308 -0
  91. package/src/generator/templates/sqlAddField.ejs +3 -0
  92. package/src/generator/templates/sqlAddForeignKey.ejs +12 -0
  93. package/src/generator/templates/sqlAddUniqueIndex.ejs +4 -0
  94. package/src/generator/templates/sqlCreateTable.ejs +9 -0
  95. package/src/generator/templates/sqlDropForeignKey.ejs +3 -0
  96. package/src/generator/templates/sqlDropUniqueIndex.ejs +4 -0
  97. package/src/generator/templates/sqlModifyField.ejs +6 -0
  98. package/src/generator/trygenerate.js +83 -0
  99. package/src/generator/worker.js +389 -0
  100. package/src/helper.js +82 -0
  101. package/src/logger.js +39 -0
  102. package/src/router.js +84 -0
  103. package/src/routers/defaultLoginApi.js +29 -0
  104. package/src/routers/defaultLoginAsset.js +18 -0
  105. package/src/routers/defaultLoginPage.js +36 -0
  106. package/src/routers/defaultRootIndex.js +16 -0
  107. package/src/routers/downloadHandler.js +51 -0
  108. package/src/routers/fileUploadApi.js +15 -0
  109. package/src/routers/generatorApi.js +30 -0
  110. package/src/routers/generatorAsset.js +18 -0
  111. package/src/routers/generatorPage.js +37 -0
  112. package/src/routers/handleError.js +43 -0
  113. package/src/routers/handleModuleNotfound.js +12 -0
  114. package/src/routers/moduleApi.js +34 -0
  115. package/src/routers/modulePage.js +102 -0
  116. package/src/sequencerdoc.js +311 -0
  117. package/src/sequencerline.js +214 -0
  118. package/src/session.js +57 -0
  119. package/src/startup.js +59 -0
  120. package/src/webapps.js +239 -0
  121. package/src/workermanager.js +83 -0
  122. package/templates/_lib_debug.ejs +11 -0
  123. package/templates/_lib_production.ejs +5 -0
  124. package/templates/application.page.ejs +143 -0
  125. package/templates/generator.page.ejs +131 -0
  126. package/templates/index.page.ejs +24 -0
  127. package/templates/login.page.ejs +102 -0
  128. package/templates/moduleError.ejs +16 -0
  129. package/templates/moduleNotfound.ejs +14 -0
  130. package/webapps.code-workspace +11 -0
@@ -0,0 +1,818 @@
1
+ import pgp from 'pg-promise';
2
+
3
+ import db from '@agung_dhewe/webapps/src/db.js'
4
+ import Api from '@agung_dhewe/webapps/src/api.js'
5
+ import sqlUtil from '@agung_dhewe/pgsqlc'
6
+ import context from '@agung_dhewe/webapps/src/context.js'
7
+ import logger from '@agung_dhewe/webapps/src/logger.js'<% if (usesequencer) { %>
8
+ import { createSequencerDocument } from '@agung_dhewe/webapps/src/sequencerdoc.js' <% } %><% if (usesequencerline) { %>
9
+ import { createSequencerLine } from '@agung_dhewe/webapps/src/sequencerline.js' <% } %><% if (importbucket) { %>
10
+ import bucket from '@agung_dhewe/webapps/src/bucket.js' <% } %>
11
+
12
+ import * as Extender from './extenders/<%= moduleName %>.apiext.js'
13
+
14
+ const moduleName = '<%= moduleName %>'
15
+ const headerSectionName = 'header'
16
+ const headerTableName = '<%= headerTableName %>' <% entitiesDetil.forEach(entity => { %>
17
+ const <%= entity.name %>TableName = '<%= entity.table %>' <% }) %>
18
+
19
+ // api: account
20
+ export default class extends Api {
21
+ constructor(req, res, next) {
22
+ super(req, res, next);
23
+ Api.cekLogin(req)
24
+ }
25
+
26
+
27
+ // dipanggil dengan model snake syntax
28
+ // contoh: header-list
29
+ // header-open-data
30
+ async init(body) { return await <%= moduleName %>_init(this, body) }
31
+
32
+ // header
33
+ async headerList(body) { return await <%= moduleName %>_headerList(this, body) }
34
+ async headerOpen(body) { return await <%= moduleName %>_headerOpen(this, body) }
35
+ async headerUpdate(body) { return await <%= moduleName %>_headerUpdate(this, body)}
36
+ async headerCreate(body) { return await <%= moduleName %>_headerCreate(this, body)}
37
+ async headerDelete(body) { return await <%= moduleName %>_headerDelete(this, body) }
38
+
39
+ <% entitiesDetil.forEach(entity => { %>
40
+ // <%= entity.name %>
41
+ async <%= entity.name %>List(body) { return await <%= moduleName %>_<%= entity.name %>List(this, body) }
42
+ async <%= entity.name %>Open(body) { return await <%= moduleName %>_<%= entity.name %>Open(this, body) }
43
+ async <%= entity.name %>Update(body) { return await <%= moduleName %>_<%= entity.name %>Update(this, body)}
44
+ async <%= entity.name %>Create(body) { return await <%= moduleName %>_<%= entity.name %>Create(this, body) }
45
+ async <%= entity.name %>Delete(body) { return await <%= moduleName %>_<%= entity.name %>Delete(this, body) }
46
+ async <%= entity.name %>DeleteRows(body) { return await <%= moduleName %>_<%= entity.name %>DeleteRows(this, body) }
47
+ <% }) %>
48
+ }
49
+
50
+ // init module
51
+ async function <%= moduleName %>_init(self, body) {
52
+ const req = self.req
53
+
54
+ // set sid untuk session ini, diperlukan ini agar session aktif
55
+ req.session.sid = req.sessionID
56
+
57
+ try {
58
+ // ambil data app dari database
59
+ const sql = 'select apps_id, apps_url from core."apps"'
60
+ const result = await db.any(sql)
61
+
62
+ const appsUrls = {}
63
+ for (let row of result) {
64
+ appsUrls[row.apps_id] = {
65
+ url: row.apps_url
66
+ }
67
+ }
68
+
69
+ return {
70
+ userId: req.session.user.userId,
71
+ userName: req.session.user.userName,
72
+ userFullname: req.session.userFullname,
73
+ sid: req.session.sid ,
74
+ notifierId: Api.generateNotifierId(moduleName, req.sessionID),
75
+ notifierSocket: req.app.locals.appConfig.notifierSocket,
76
+ appsUrls: appsUrls
77
+ }
78
+
79
+ } catch (err) {
80
+ throw err
81
+ }
82
+ }
83
+
84
+
85
+ // data logging
86
+ async function <%= moduleName %>_log(self, body, startTime, tablename, id, action, data={}, remark='') {
87
+ const { source } = body
88
+ const req = self.req
89
+ const user_id = req.session.user.userId
90
+ const user_name = req.session.user.userFullname
91
+ const ipaddress = req.ip
92
+ const metadata = JSON.stringify({...{source:source}, ...data})
93
+ const endTime = process.hrtime.bigint();
94
+ const executionTimeMs = Number((endTime - startTime) / 1_000_000n); // hasil dalam ms tanpa desimal
95
+
96
+ const logdata = {id, user_id, user_name, moduleName, action, tablename, executionTimeMs, remark, metadata, ipaddress}
97
+ const ret = await logger.log(logdata)
98
+ return ret
99
+ }
100
+
101
+ <% if (importbucket) { %>
102
+ async function <%= moduleName %>_upload(self, data, files, detilName, id, fn_uploadSuccess) {
103
+ const bucketname = '<%= moduleName %>'
104
+ const docid = data.<%= headerPrimaryKey %>
105
+
106
+ const uploads = files.map(async (file) => {
107
+ const fieldname = file.fieldname
108
+ const objectname = detilName=='header' ? `${docid}/${fieldname}` : `${docid}/${detilName}/${id}-${fieldname}`
109
+
110
+ const metaData = {
111
+ 'Content-Type': file.mimetype,
112
+ 'X-Amz-Meta-document_id': docid,
113
+ 'X-Amz-Meta-fieldname': fieldname,
114
+ 'X-Amz-Meta-originalname': file.originalname,
115
+ };
116
+
117
+ if (detilName!='header') {
118
+ metaData['X-Amz-Meta-document-rowid'] = id
119
+ }
120
+
121
+
122
+ // simpan di bucket
123
+ await bucket.putObject(bucketname, objectname, file.buffer, metaData);
124
+
125
+ // catat di log
126
+ if (typeof fn_uploadSuccess==='function') {
127
+ fn_uploadSuccess({
128
+ originalname: file.originalname,
129
+ mimetype: file.fieldname,
130
+ size: file.size,
131
+ bucketname,
132
+ objectname
133
+ })
134
+ }
135
+ return {fieldname, objectname};
136
+ });
137
+
138
+ const uploadedFiles = await Promise.all(uploads);
139
+ return uploadedFiles
140
+ }
141
+ <% } %>
142
+
143
+ async function <%= moduleName %>_headerList(self, body) {
144
+ const tablename = headerTableName
145
+ const { criteria={}, limit=0, offset=0, columns=[], sort={} } = body
146
+ const searchMap = {<% headerSearchMap.forEach(searchmap => { %>
147
+ <%= searchmap.name %>: `<%- searchmap.data %>`,<% }) %>
148
+ };
149
+
150
+ try {
151
+
152
+ // jika tidak ada default searchtext
153
+ if (searchMap.searchtext===undefined) {
154
+ throw new Error(`'searchtext' belum didefinisikan di searchMap`)
155
+ }
156
+
157
+
158
+ // hilangkan criteria '' atau null
159
+ for (var cname in criteria) {
160
+ if (criteria[cname]==='' || criteria[cname]===null) {
161
+ delete criteria[cname]
162
+ }
163
+ }
164
+
165
+ // apabila ada keperluan untuk recompose criteria
166
+ if (typeof Extender.headerListCriteria === 'function') {
167
+ await Extender.headerListCriteria(self, db, searchMap, criteria, sort, columns)
168
+ }
169
+
170
+ var max_rows = limit==0 ? 10 : limit
171
+ const {whereClause, queryParams} = sqlUtil.createWhereClause(criteria, searchMap)
172
+ const sql = sqlUtil.createSqlSelect({tablename, columns, whereClause, sort, limit:max_rows+1, offset, queryParams})
173
+ const rows = await db.any(sql, queryParams);
174
+
175
+
176
+ var i = 0
177
+ const data = []
178
+ for (var row of rows) {
179
+ i++
180
+ if (i>max_rows) { break }
181
+
182
+ <% headerFieldsLookup.forEach(lookup => { %>// lookup: <%= lookup.bindingDisplay %> dari field <%= lookup.bindingText %> pada table <%= lookup.table %> dimana (<%= lookup.table %>.<%= lookup.bindingValue %> = <%= headerTableName %>.<%=lookup.fieldname%>)
183
+ {
184
+ const { <%= lookup.bindingText %> } = await sqlUtil.lookupdb(db, '<%= lookup.table %>', '<%=lookup.bindingValue%>', row.<%=lookup.fieldname%>)
185
+ row.<%= lookup.bindingDisplay %> = <%= lookup.bindingText %>
186
+ }
187
+ <% }) %>
188
+ // pasang extender di sini
189
+ if (typeof Extender.headerListRow === 'function') {
190
+ await Extender.headerListRow(self, row)
191
+ }
192
+
193
+ data.push(row)
194
+ }
195
+
196
+ var nextoffset = null
197
+ if (rows.length>max_rows) {
198
+ nextoffset = offset+max_rows
199
+ }
200
+
201
+ return {
202
+ criteria: criteria,
203
+ limit: max_rows,
204
+ nextoffset: nextoffset,
205
+ data: data
206
+ }
207
+
208
+ } catch (err) {
209
+ throw err
210
+ }
211
+ }
212
+
213
+ async function <%= moduleName %>_headerOpen(self, body) {
214
+ const tablename = headerTableName
215
+
216
+ try {
217
+ const { id } = body
218
+ const criteria = { <%= headerPrimaryKey %>: id }
219
+ const searchMap = { <%= headerPrimaryKey %>: `<%= headerPrimaryKey %> = \${<%= headerPrimaryKey %>}`}
220
+ const {whereClause, queryParams} = sqlUtil.createWhereClause(criteria, searchMap)
221
+ const sql = sqlUtil.createSqlSelect({
222
+ tablename: tablename,
223
+ columns:[],
224
+ whereClause,
225
+ sort:{},
226
+ limit:0,
227
+ offset:0,
228
+ queryParams
229
+ })
230
+ const data = await db.one(sql, queryParams);
231
+ if (data==null) {
232
+ throw new Error(`[${tablename}] data dengan id '${id}' tidak ditemukan`)
233
+ }
234
+
235
+ <% headerFieldsLookup.forEach(lookup => { %>// lookup: <%= lookup.bindingDisplay %> dari field <%= lookup.bindingText %> pada table <%= lookup.table %> dimana (<%= lookup.table %>.<%= lookup.bindingValue %> = <%= headerTableName %>.<%=lookup.fieldname%>)
236
+ {
237
+ const { <%= lookup.bindingText %> } = await sqlUtil.lookupdb(db, '<%= lookup.table %>', '<%=lookup.bindingValue%>', data.<%=lookup.fieldname%>)
238
+ data.<%= lookup.bindingDisplay %> = <%= lookup.bindingText %>
239
+ }
240
+ <% }) %>
241
+
242
+ // lookup data createby
243
+ {
244
+ const { user_fullname } = await sqlUtil.lookupdb(db, 'core.user', 'user_id', data._createby)
245
+ data._createby = user_fullname ?? ''
246
+ }
247
+
248
+ // lookup data modifyby
249
+ {
250
+ const { user_fullname } = await sqlUtil.lookupdb(db, 'core.user', 'user_id', data._modifyby)
251
+ data._modifyby = user_fullname ?? ''
252
+ }
253
+
254
+ // pasang extender untuk olah data
255
+ if (typeof Extender.headerOpen === 'function') {
256
+ await Extender.headerOpen(self, data)
257
+ }
258
+
259
+ return data
260
+ } catch (err) {
261
+ throw err
262
+ }
263
+ }
264
+
265
+
266
+ async function <%= moduleName %>_headerCreate(self, body) {
267
+ const { source='<%= moduleName %>', data={} } = body
268
+ const req = self.req
269
+ const user_id = req.session.user.userId
270
+ const startTime = process.hrtime.bigint();
271
+ const tablename = headerTableName
272
+
273
+ try {
274
+
275
+ // parse uploaded data
276
+ const files = Api.parseUploadData(data, req.files)
277
+
278
+
279
+ data._createby = user_id
280
+ data._createdate = (new Date()).toISOString()
281
+
282
+ const result = await db.tx(async tx=>{
283
+ sqlUtil.connect(tx)
284
+
285
+ <% if (usesequencer) { %>// buat sequencer document
286
+ const sequencer = createSequencerDocument(tx, {
287
+ COMPANY_CODE: req.app.locals.appConfig.COMPANY_CODE,
288
+ blockLength: <%= identifierBlock %>,
289
+ numberLength: <%= identifierLength %>,
290
+ })
291
+
292
+ if (typeof Extender.sequencerSetup === 'function') {
293
+ // jika ada keperluan menambahkan code block/cluster di sequencer
294
+ // dapat diimplementasikan di exterder sequencerSetup
295
+ await Extender.sequencerSetup(self, tx, sequencer, data)
296
+ }
297
+
298
+ <%if (yearly) { %>// generate data <%= identifierPrefix %> reset pertahun
299
+ const seqdata = await sequencer.yearly('<%= identifierPrefix %>')<% }
300
+ else {
301
+
302
+ %>// generate data <%= identifierPrefix %> reset perbulan
303
+ const seqdata = await sequencer.monthly('<%= identifierPrefix %>')<% } %>
304
+ data.<%= headerPrimaryKey %> = seqdata.id
305
+
306
+ // apabila ada keperluan pengelohan data sebelum disimpan, lakukan di extender headerCreating
307
+ if (typeof Extender.headerCreating === 'function') {
308
+ await Extender.headerCreating(self, tx, data, seqdata)
309
+ }
310
+
311
+ <% } else if (usesequencerline) { %>
312
+ // buat short sequencer
313
+ const sequencer = createSequencerLine(tx, {})
314
+
315
+ if (typeof Extender.sequencerSetup === 'function') {
316
+ // jika ada keperluan menambahkan code block/cluster di sequencer
317
+ // dapat diimplementasikan di exterder sequencerSetup
318
+ await Extender.sequencerSetup(self, tx, sequencer, data)
319
+ }
320
+
321
+ // generate short id untuk <%= identifierPrefix %> reset pertahun
322
+ const id = await sequencer.yearlyshort('<%= identifierPrefix %>')
323
+ data.<%= headerPrimaryKey %> = id
324
+
325
+ // apabila ada keperluan pengelohan data sebelum disimpan, lakukan di extender headerCreating
326
+ if (typeof Extender.headerCreating === 'function') {
327
+ await Extender.headerCreating(self, tx, data)
328
+ }
329
+
330
+ <% } else { %>
331
+ // apabila ada keperluan pengelohan data sebelum disimpan, lakukan di extender headerCreating
332
+ if (typeof Extender.headerCreating === 'function') {
333
+ await Extender.headerCreating(self, tx, data)
334
+ }<% } %>
335
+
336
+ <% if (autoid) {
337
+ %><% if (usesequencer || usesequencerline) { %>const cmd = sqlUtil.createInsertCommand(tablename, data)<% } else { %>const cmd = sqlUtil.createInsertCommand(tablename, data, ['<%= headerPrimaryKey %>'])<% } %><%
338
+ } else { %>const cmd = sqlUtil.createInsertCommand(tablename, data)<% } %>
339
+ const ret = await cmd.execute(data)
340
+
341
+ <% if (headerHasUpload) { %>
342
+ // jika ada file yang diupload di header
343
+ if (files!=null) {
344
+ <%= moduleName %>_upload(self, ret, files, 'header', ret.<%= headerPrimaryKey %>, (result)=>{
345
+ <%= moduleName %>_log(self, body, startTime, tablename, ret.<%= headerPrimaryKey %>, 'UPLOAD FILE', result, `${result.originalname}`)
346
+ })
347
+ }
348
+ <% } %>
349
+ const logMetadata = {}
350
+
351
+ // apabila ada keperluan pengelohan data setelah disimpan, lakukan di extender headerCreated
352
+ if (typeof Extender.headerCreated === 'function') {
353
+ await Extender.headerCreated(self, tx, ret, data, logMetadata)
354
+ }
355
+
356
+ // record log
357
+ <%= moduleName %>_log(self, body, startTime, tablename, ret.<%= headerPrimaryKey %>, 'CREATE', logMetadata)
358
+
359
+ return ret
360
+ })
361
+
362
+ return result
363
+ } catch (err) {
364
+ throw err
365
+ }
366
+ }
367
+
368
+ async function <%= moduleName %>_headerUpdate(self, body) {
369
+ const { source='<%= moduleName %>', data={} } = body
370
+ const req = self.req
371
+ const user_id = req.session.user.userId
372
+ const startTime = process.hrtime.bigint()
373
+ const tablename = headerTableName
374
+
375
+ try {
376
+
377
+ // parse uploaded data
378
+ const files = Api.parseUploadData(data, req.files)
379
+
380
+
381
+ data._modifyby = user_id
382
+ data._modifydate = (new Date()).toISOString()
383
+
384
+ const result = await db.tx(async tx=>{
385
+ sqlUtil.connect(tx)
386
+
387
+
388
+ // apabila ada keperluan pengelohan data sebelum disimpan, lakukan di extender headerCreating
389
+ if (typeof Extender.headerUpdating === 'function') {
390
+ await Extender.headerUpdating(self, tx, data)
391
+ }
392
+
393
+ // eksekusi update
394
+ const cmd = sqlUtil.createUpdateCommand(tablename, data, ['<%= headerPrimaryKey %>'])
395
+ const ret = await cmd.execute(data)
396
+
397
+ <% if (headerHasUpload) { %>
398
+ // jika ada file yang diupload di header
399
+ if (files!=null) {
400
+ <%= moduleName %>_upload(self, ret, files, 'header', ret.<%= headerPrimaryKey %>, (result)=>{
401
+ <%= moduleName %>_log(self, body, startTime, tablename, ret.<%= headerPrimaryKey %>, 'UPLOAD FILE', result, `${result.originalname}`)
402
+ })
403
+ }
404
+ <% } %>
405
+ const logMetadata = {}
406
+
407
+ // apabila ada keperluan pengelohan data setelah disimpan, lakukan di extender headerCreated
408
+ if (typeof Extender.headerUpdated === 'function') {
409
+ await Extender.headerUpdated(self, tx, ret, data, logMetadata)
410
+ }
411
+
412
+ // record log
413
+ <%= moduleName %>_log(self, body, startTime, tablename, data.<%= headerPrimaryKey %>, 'UPDATE')
414
+
415
+ return ret
416
+ })
417
+
418
+
419
+ return result
420
+ } catch (err) {
421
+ throw err
422
+ }
423
+ }
424
+
425
+
426
+ async function <%= moduleName %>_headerDelete(self, body) {
427
+ const { source, id } = body
428
+ const req = self.req
429
+ const user_id = req.session.user.userId
430
+ const startTime = process.hrtime.bigint()
431
+ const tablename = headerTableName
432
+
433
+ try {
434
+
435
+ const deletedRow = await db.tx(async tx=>{
436
+ sqlUtil.connect(tx)
437
+
438
+ const dataToRemove = {<%= headerPrimaryKey %>: id}
439
+
440
+ // apabila ada keperluan pengelohan data sebelum dihapus, lakukan di extender headerDeleting
441
+ if (typeof Extender.headerDeleting === 'function') {
442
+ await Extender.headerDeleting(self, tx, dataToRemove)
443
+ }
444
+
445
+ <% if (entitiesDetil.length>0) { %>
446
+ <% entitiesDetil.forEach(entity => { %>// hapus data <%= entity.name %>
447
+ {
448
+ const sql = `select * from ${<%= entity.name %>TableName} where <%= headerPrimaryKey %>=\${<%= headerPrimaryKey %>}`
449
+ const rows = await tx.any(sql, dataToRemove)
450
+ for (let row<%= entity.name %> of rows) {
451
+ // apabila ada keperluan pengelohan data sebelum dihapus, lakukan di extender
452
+ if (typeof Extender.<%= entity.name %>Deleting === 'function') {
453
+ await Extender.<%= entity.name %>Deleting(self, tx, row<%= entity.name %>, logMetadata)
454
+ }
455
+
456
+ const param = {<%= entity.pk %>: row<%= entity.name %>.<%= entity.pk %>}
457
+ const cmd = sqlUtil.createDeleteCommand(<%= entity.name %>TableName, ['<%= entity.pk %>'])
458
+ const deletedRow = await cmd.execute(param)
459
+
460
+ // apabila ada keperluan pengelohan data setelah dihapus, lakukan di extender
461
+ if (typeof Extender.<%= entity.name %>Deleted === 'function') {
462
+ await Extender.<%= entity.name %>Deleted(self, tx, deletedRow, logMetadata)
463
+ }
464
+
465
+ <%= moduleName %>_log(self, body, startTime, <%= entity.name %>TableName, row<%= entity.name %>.<%= entity.pk %>, 'DELETE', {rowdata: deletedRow})
466
+ <%= moduleName %>_log(self, body, startTime, headerTableName, row<%= entity.name %>.<%= headerPrimaryKey %>, 'DELETE ROW <%= entity.name.toUpperCase() %>', {<%= entity.pk %>: row<%= entity.name %>.<%= entity.pk %>, tablename: <%= entity.name %>TableName}, `removed: ${row<%= entity.name %>.<%= entity.pk %>}`)
467
+
468
+
469
+ }
470
+ }
471
+
472
+ <% }) %>
473
+ <% } %>
474
+
475
+ // hapus data header
476
+ const cmd = sqlUtil.createDeleteCommand(tablename, ['<%= headerPrimaryKey %>'])
477
+ const deletedRow = await cmd.execute(dataToRemove)
478
+
479
+ const logMetadata = {}
480
+
481
+ // apabila ada keperluan pengelohan data setelah dihapus, lakukan di extender headerDeleted
482
+ if (typeof Extender.headerDeleted === 'function') {
483
+ await Extender.headerDeleted(self, tx, ret, logMetadata)
484
+ }
485
+
486
+ // record log
487
+ <%= moduleName %>_log(self, body, startTime, tablename, id, 'DELETE', logMetadata)
488
+
489
+ return deletedRow
490
+ })
491
+
492
+
493
+ return deletedRow
494
+ } catch (err) {
495
+ throw err
496
+ }
497
+ }
498
+
499
+
500
+ <% entitiesDetil.forEach(entity => { %>
501
+ // <%= entity.name %>
502
+
503
+ async function <%= moduleName %>_<%= entity.name %>List(self, body) {
504
+ const tablename = <%= entity.name %>TableName
505
+ const { criteria={}, limit=0, offset=0, columns=[], sort={} } = body
506
+ const searchMap = {
507
+ <%= headerPrimaryKey %>: `<%= headerPrimaryKey %>=try_cast_bigint(\${<%= headerPrimaryKey %>}, 0)`,
508
+ };
509
+
510
+
511
+ try {
512
+
513
+ // hilangkan criteria '' atau null
514
+ for (var cname in criteria) {
515
+ if (criteria[cname]==='' || criteria[cname]===null) {
516
+ delete criteria[cname]
517
+ }
518
+ }
519
+
520
+ // apabila ada keperluan untuk recompose criteria
521
+ if (typeof Extender.<%= moduleName %>ListCriteria === 'function') {
522
+ await Extender.<%= moduleName %>ListCriteria(self, db, searchMap, criteria, sort, columns)
523
+ }
524
+
525
+ var max_rows = limit==0 ? 10 : limit
526
+ const {whereClause, queryParams} = sqlUtil.createWhereClause(criteria, searchMap)
527
+ const sql = sqlUtil.createSqlSelect({tablename, columns, whereClause, sort, limit:max_rows+1, offset, queryParams})
528
+ const rows = await db.any(sql, queryParams);
529
+
530
+
531
+ var i = 0
532
+ const data = []
533
+ for (var row of rows) {
534
+ i++
535
+ if (i>max_rows) { break }
536
+
537
+ <% entity.fieldsLookup.forEach(lookup => { %>// lookup: <%= lookup.bindingDisplay %> dari field <%= lookup.bindingText %> pada table <%= lookup.table %> dimana (<%= lookup.table %>.<%= lookup.bindingValue %> = <%= headerTableName %>.<%=lookup.fieldname%>)
538
+ {
539
+ const { <%= lookup.bindingText %> } = await sqlUtil.lookupdb(db, '<%= lookup.table %>', '<%=lookup.bindingValue%>', row.<%=lookup.fieldname%>)
540
+ row.<%= lookup.bindingDisplay %> = <%= lookup.bindingText %>
541
+ }
542
+ <% }) %>
543
+
544
+ // pasang extender di sini
545
+ if (typeof Extender.detilListRow === 'function') {
546
+ await Extender.detilListRow(row)
547
+ }
548
+
549
+ data.push(row)
550
+ }
551
+
552
+ var nextoffset = null
553
+ if (rows.length>max_rows) {
554
+ nextoffset = offset+max_rows
555
+ }
556
+
557
+ return {
558
+ criteria: criteria,
559
+ limit: max_rows,
560
+ nextoffset: nextoffset,
561
+ data: data
562
+ }
563
+
564
+ } catch (err) {
565
+ throw err
566
+ }
567
+ }
568
+
569
+ async function <%= moduleName %>_<%= entity.name %>Open(self, body) {
570
+ const tablename = <%= entity.name %>TableName
571
+
572
+ try {
573
+ const { id } = body
574
+ const criteria = { <%= entity.pk %>: id }
575
+ const searchMap = { <%= entity.pk %>: `<%= entity.pk %> = \${<%= entity.pk %>}`}
576
+ const {whereClause, queryParams} = sqlUtil.createWhereClause(criteria, searchMap)
577
+ const sql = sqlUtil.createSqlSelect({
578
+ tablename,
579
+ columns:[],
580
+ whereClause,
581
+ sort:{},
582
+ limit:0,
583
+ offset:0,
584
+ queryParams
585
+ })
586
+ const data = await db.one(sql, queryParams);
587
+ if (data==null) {
588
+ throw new Error(`[${tablename}] data dengan id '${id}' tidak ditemukan`)
589
+ }
590
+
591
+
592
+ <% entity.fieldsLookup.forEach(lookup => { %>// lookup: <%= lookup.bindingDisplay %> dari field <%= lookup.bindingText %> pada table <%= lookup.table %> dimana (<%= lookup.table %>.<%= lookup.bindingValue %> = <%= headerTableName %>.<%=lookup.fieldname%>)
593
+ {
594
+ const { <%= lookup.bindingText %> } = await sqlUtil.lookupdb(db, '<%= lookup.table %>', '<%=lookup.bindingValue%>', data.<%=lookup.fieldname%>)
595
+ data.<%= lookup.bindingDisplay %> = <%= lookup.bindingText %>
596
+ }
597
+ <% }) %>
598
+
599
+ // lookup data createby
600
+ {
601
+ const { user_fullname } = await sqlUtil.lookupdb(db, 'core.user', 'user_id', data._createby)
602
+ data._createby = user_fullname ?? ''
603
+ }
604
+
605
+ // lookup data modifyby
606
+ {
607
+ const { user_fullname } = await sqlUtil.lookupdb(db, 'core.user', 'user_id', data._modifyby)
608
+ data._modifyby = user_fullname ?? ''
609
+ }
610
+
611
+ return data
612
+ } catch (err) {
613
+ throw err
614
+ }
615
+ }
616
+
617
+ async function <%= moduleName %>_<%= entity.name %>Create(self, body) {
618
+ const { source='<%= moduleName %>', data={} } = body
619
+ const req = self.req
620
+ const user_id = req.session.user.userId
621
+ const startTime = process.hrtime.bigint();
622
+ const tablename = <%= entity.name %>TableName
623
+
624
+ try {
625
+
626
+ // parse uploaded data
627
+ const files = Api.parseUploadData(data, req.files)
628
+
629
+
630
+ data._createby = user_id
631
+ data._createdate = (new Date()).toISOString()
632
+
633
+ const result = await db.tx(async tx=>{
634
+ sqlUtil.connect(tx)
635
+
636
+ const sequencer = createSequencerLine(tx, {})
637
+ const id = await sequencer.increment('<%= identifierPrefix %>')
638
+ data.<%= entity.pk %> = id
639
+
640
+ // apabila ada keperluan pengolahan data SEBELUM disimpan
641
+ if (typeof Extender.<%= entity.name %>Creating === 'function') {
642
+ await Extender.<%= entity.name %>Creating(self, tx, data)
643
+ }
644
+
645
+ const cmd = sqlUtil.createInsertCommand(tablename, data)
646
+ const ret = await cmd.execute(data)
647
+ <% if (entity.detilHasUpload) { %>
648
+ // jika ada file yang diupload di <%= entity.name %>
649
+ if (files!=null) {
650
+ <%= moduleName %>_upload(self, ret, files, '<%= entity.name %>', ret.<%= entity.pk %>, (result)=>{
651
+ <%= moduleName %>_log(self, body, startTime, tablename, ret.<%= entity.pk %>, 'UPLOAD FILE', result, `${result.originalname}`)
652
+ })
653
+ }
654
+ <% } %>
655
+ const logMetadata = {}
656
+
657
+ // apabila ada keperluan pengelohan data setelah disimpan, lakukan di extender headerCreated
658
+ if (typeof Extender.<%= entity.name %>Created === 'function') {
659
+ await Extender.<%= entity.name %>Created(self, tx, ret, data, logMetadata)
660
+ }
661
+
662
+ // record log
663
+ <%= moduleName %>_log(self, body, startTime, tablename, ret.<%= entity.pk %>, 'CREATE', logMetadata)
664
+
665
+ return ret
666
+ })
667
+
668
+ return result
669
+ } catch (err) {
670
+ throw err
671
+ }
672
+ }
673
+
674
+ async function <%= moduleName %>_<%= entity.name %>Update(self, body) {
675
+ const { source='<%= moduleName %>', data={} } = body
676
+ const req = self.req
677
+ const user_id = req.session.user.userId
678
+ const startTime = process.hrtime.bigint()
679
+ const tablename = <%= entity.name %>TableName
680
+
681
+ try {
682
+
683
+ // parse uploaded data
684
+ const files = Api.parseUploadData(data, req.files)
685
+
686
+
687
+ data._modifyby = user_id
688
+ data._modifydate = (new Date()).toISOString()
689
+
690
+ const result = await db.tx(async tx=>{
691
+ sqlUtil.connect(tx)
692
+
693
+
694
+ // apabila ada keperluan pengolahan data SEBELUM disimpan
695
+ if (typeof Extender.<%= entity.name %>Updating === 'function') {
696
+ await Extender.<%= entity.name %>Updating(self, tx, data)
697
+ }
698
+
699
+ const cmd = sqlUtil.createUpdateCommand(tablename, data, ['<%= entity.pk %>'])
700
+ const ret = await cmd.execute(data)
701
+ <% if (entity.detilHasUpload) { %>
702
+ // jika ada file yang diupload di <%= entity.name %>
703
+ if (files!=null) {
704
+ <%= moduleName %>_upload(self, ret, files, '<%= entity.name %>', ret.<%= entity.pk %>, (result)=>{
705
+ <%= moduleName %>_log(self, body, startTime, tablename, ret.<%= entity.pk %>, 'UPLOAD FILE', result, `${result.originalname}`)
706
+ })
707
+ }
708
+ <% } %>
709
+ const logMetadata = {}
710
+
711
+ // apabila ada keperluan pengelohan data setelah disimpan, lakukan di extender headerCreated
712
+ if (typeof Extender.<%= entity.name %>Updated === 'function') {
713
+ await Extender.<%= entity.name %>Updated(self, tx, ret, data, logMetadata)
714
+ }
715
+
716
+ // record log
717
+ <%= moduleName %>_log(self, body, startTime, tablename, data.<%= entity.pk %>, 'UPDATE', logMetadata)
718
+
719
+ return ret
720
+ })
721
+
722
+ return result
723
+ } catch (err) {
724
+ throw err
725
+ }
726
+ }
727
+
728
+ async function <%= moduleName %>_<%= entity.name %>Delete(self, body) {
729
+ const { source, id } = body
730
+ const req = self.req
731
+ const user_id = req.session.user.userId
732
+ const startTime = process.hrtime.bigint()
733
+ const tablename = <%= entity.name %>TableName
734
+
735
+ try {
736
+
737
+ const deletedRow = await db.tx(async tx=>{
738
+ sqlUtil.connect(tx)
739
+
740
+ const dataToRemove = {<%= entity.pk %>: id}
741
+ const sql = `select * from ${<%= entity.name %>TableName} where <%= entity.pk %>=\${<%= entity.pk %>}`
742
+ const row<%= entity.name %> = await tx.oneOrNone(sql, dataToRemove)
743
+
744
+
745
+ // apabila ada keperluan pengelohan data sebelum dihapus, lakukan di extender
746
+ if (typeof Extender.<%= entity.name %>Deleting === 'function') {
747
+ await Extender.<%= entity.name %>Deleting(self, tx, row<%= entity.name %>, logMetadata)
748
+ }
749
+
750
+ const param = {<%= entity.pk %>: row<%= entity.name %>.<%= entity.pk %>}
751
+ const cmd = sqlUtil.createDeleteCommand(<%= entity.name %>TableName, ['<%= entity.pk %>'])
752
+ const deletedRow = await cmd.execute(param)
753
+
754
+ // apabila ada keperluan pengelohan data setelah dihapus, lakukan di extender
755
+ if (typeof Extender.<%= entity.name %>Deleted === 'function') {
756
+ await Extender.<%= entity.name %>Deleted(self, tx, deletedRow, logMetadata)
757
+ }
758
+
759
+ <%= moduleName %>_log(self, body, startTime, <%= entity.name %>TableName, row<%= entity.name %>.<%= entity.pk %>, 'DELETE', {rowdata: deletedRow})
760
+ <%= moduleName %>_log(self, body, startTime, headerTableName, row<%= entity.name %>.<%= headerPrimaryKey %>, 'DELETE ROW <%= entity.name.toUpperCase() %>', {<%= entity.pk %>: row<%= entity.name %>.<%= entity.pk %>, tablename: <%= entity.name %>TableName}, `removed: ${row<%= entity.name %>.<%= entity.pk %>}`)
761
+
762
+ return deletedRow
763
+ })
764
+
765
+
766
+ return deletedRow
767
+ } catch (err) {
768
+ throw err
769
+ }
770
+ }
771
+
772
+ async function <%= moduleName %>_<%= entity.name %>DeleteRows(self, body) {
773
+ const { data } = body
774
+ const req = self.req
775
+ const user_id = req.session.user.userId
776
+ const startTime = process.hrtime.bigint();
777
+ const tablename = <%= entity.name %>TableName
778
+
779
+
780
+ try {
781
+ const result = await db.tx(async tx=>{
782
+ sqlUtil.connect(tx)
783
+
784
+ for (let id of data) {
785
+ const dataToRemove = {<%= entity.pk %>: id}
786
+ const sql = `select * from ${<%= entity.name %>TableName} where <%= entity.pk %>=\${<%= entity.pk %>}`
787
+ const row<%= entity.name %> = await tx.oneOrNone(sql, dataToRemove)
788
+
789
+ // apabila ada keperluan pengelohan data sebelum dihapus, lakukan di extender
790
+ if (typeof Extender.<%= entity.name %>Deleting === 'function') {
791
+ await Extender.<%= entity.name %>Deleting(self, tx, row<%= entity.name %>, logMetadata)
792
+ }
793
+
794
+ const param = {<%= entity.pk %>: row<%= entity.name %>.<%= entity.pk %>}
795
+ const cmd = sqlUtil.createDeleteCommand(<%= entity.name %>TableName, ['<%= entity.pk %>'])
796
+ const deletedRow = await cmd.execute(param)
797
+
798
+ // apabila ada keperluan pengelohan data setelah dihapus, lakukan di extender
799
+ if (typeof Extender.<%= entity.name %>Deleted === 'function') {
800
+ await Extender.<%= entity.name %>Deleted(self, tx, deletedRow, logMetadata)
801
+ }
802
+
803
+ <%= moduleName %>_log(self, body, startTime, <%= entity.name %>TableName, row<%= entity.name %>.<%= entity.pk %>, 'DELETE', {rowdata: deletedRow})
804
+ <%= moduleName %>_log(self, body, startTime, headerTableName, row<%= entity.name %>.<%= headerPrimaryKey %>, 'DELETE ROW <%= entity.name.toUpperCase() %>', {<%= entity.pk %>: row<%= entity.name %>.<%= entity.pk %>, tablename: <%= entity.name %>TableName}, `removed: ${row<%= entity.name %>.<%= entity.pk %>}`)
805
+ }
806
+ })
807
+
808
+ const res = {
809
+ deleted: true,
810
+ message: ''
811
+ }
812
+ return res
813
+ } catch (err) {
814
+ throw err
815
+ }
816
+ }
817
+
818
+ <% }) %>