@simitgroup/simpleapp-generator 1.1.12 → 1.1.15

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 (96) hide show
  1. package/dist/buildinschemas/autoincreament.d.ts.map +1 -1
  2. package/dist/buildinschemas/autoincreament.js +1 -2
  3. package/dist/buildinschemas/autoincreament.js.map +1 -1
  4. package/dist/buildinschemas/docnoformat.d.ts.map +1 -1
  5. package/dist/buildinschemas/docnoformat.js +0 -1
  6. package/dist/buildinschemas/docnoformat.js.map +1 -1
  7. package/dist/buildinschemas/permission.d.ts.map +1 -1
  8. package/dist/buildinschemas/permission.js +0 -1
  9. package/dist/buildinschemas/permission.js.map +1 -1
  10. package/dist/framework.js +4 -4
  11. package/dist/framework.js.map +1 -1
  12. package/dist/generate.d.ts.map +1 -1
  13. package/dist/generate.js +20 -7
  14. package/dist/generate.js.map +1 -1
  15. package/dist/processors/bpmnbuilder.d.ts +2 -0
  16. package/dist/processors/bpmnbuilder.d.ts.map +1 -0
  17. package/dist/processors/bpmnbuilder.js +151 -0
  18. package/dist/processors/bpmnbuilder.js.map +1 -0
  19. package/dist/resource/camunda-moodle.d.ts +27 -0
  20. package/dist/resource/camunda-moodle.d.ts.map +1 -0
  21. package/dist/resource/camunda-moodle.js +91 -0
  22. package/dist/resource/camunda-moodle.js.map +1 -0
  23. package/dist/type.d.ts +0 -1
  24. package/dist/type.d.ts.map +1 -1
  25. package/dist/type.js.map +1 -1
  26. package/docs/backend.md +1 -1
  27. package/package.json +3 -1
  28. package/src/buildinschemas/autoincreament.ts +2 -3
  29. package/src/buildinschemas/docnoformat.ts +0 -1
  30. package/src/buildinschemas/permission.ts +0 -1
  31. package/src/framework.ts +4 -4
  32. package/src/generate.ts +25 -7
  33. package/src/processors/bpmnbuilder.ts +148 -0
  34. package/src/resource/camunda-moodle.ts +87 -0
  35. package/src/type.ts +1 -2
  36. package/templates/basic/nest/controller.ts.eta +3 -3
  37. package/templates/basic/nest/service.ts.eta +4 -6
  38. package/templates/basic/nuxt/pages.[id].vue.eta +4 -3
  39. package/templates/basic/nuxt/pages.form.vue.eta +0 -1
  40. package/templates/basic/nuxt/pages.landing.vue.eta +11 -11
  41. package/templates/basic/nuxt/pages.new.vue.eta +4 -3
  42. package/templates/basic/nuxt/pages.viewer.vue.eta +4 -7
  43. package/templates/nest/.env._eta +1 -1
  44. package/templates/nest/src/app.module.ts.eta +20 -2
  45. package/templates/nest/src/simpleapp/.gitignore.eta +2 -1
  46. package/templates/nest/src/simpleapp/generate/commons/user.context.ts.eta +23 -4
  47. package/templates/nest/src/simpleapp/generate/processors/simpleapp.processor.ts.eta +356 -294
  48. package/templates/nest/src/simpleapp/generate/workflow/formschema/SimpleApproveReject.ts.eta +8 -0
  49. package/templates/nest/src/simpleapp/generate/workflow/formschema/index.ts.eta +1 -0
  50. package/templates/nest/src/simpleapp/{workflow → generate/workflow}/workflow.apischema.ts.eta +17 -22
  51. package/templates/nest/src/simpleapp/generate/workflow/workflow.config.ts.eta +57 -0
  52. package/templates/nest/src/simpleapp/{workflow → generate/workflow}/workflow.controller.ts.eta +5 -6
  53. package/templates/nest/src/simpleapp/generate/workflow/workflow.delegate.ts.eta +153 -0
  54. package/templates/nest/src/simpleapp/{workflow → generate/workflow}/workflow.service.ts.eta +62 -75
  55. package/templates/nest/src/simpleapp/{workflow → generate/workflow}/workflow.type.ts.eta +24 -20
  56. package/templates/nest/src/simpleapp/simpleapp.module.ts.eta +16 -6
  57. package/templates/nest/src/simpleapp/workflows/bpmn/readme.md._eta +1 -0
  58. package/templates/nest/src/simpleapp/workflows/delegates/simpleapp.delegate.ts._eta +27 -0
  59. package/templates/nest/src/simpleapp/workflows/readme.md._eta +1 -0
  60. package/templates/nuxt/.gitignore.eta +5 -4
  61. package/templates/nuxt/components/header/button/task/HeaderButtonTaskList.vue.eta +47 -65
  62. package/templates/nuxt/components/simpleApp/SimpleAppForm.vue.eta +1 -1
  63. package/templates/nuxt/components/simpleApp/SimpleAppJsonSchemaForm.vue.eta +58 -23
  64. package/templates/nuxt/components/workflow/forms/dynamicfield.vue._eta +85 -0
  65. package/templates/nuxt/components/workflow/forms/index.ts._eta +10 -0
  66. package/templates/nuxt/components/workflow/forms/simpleapprove.vue._eta +43 -0
  67. package/templates/nuxt/lang/{df.ts.eta → df.ts._eta} +6 -1
  68. package/templates/nuxt/nuxt.config.ts.eta +1 -1
  69. package/templates/nuxt/simpleapp/workflows/bpmn/readme.md._eta +1 -0
  70. package/templates/nuxt/simpleapp/workflows/forms/readme.md._eta +1 -0
  71. package/templates/nuxt/types/schema.ts.eta +1 -2
  72. package/templates/project/groups/admin.json.eta +1 -1
  73. package/templates/{nest/src/simpleapp/workflow/bpmn/suspendcustomer.bpmn.eta → project/workflows/bpmn/suspendcustomer.bpmn._eta} +20 -20
  74. package/templates/project/workflows/forms/index.ts.eta +2 -0
  75. package/templates/project/workflows/forms/simpleapprove.jsonschema.ts.eta +8 -0
  76. package/templates/workflow/next/delegate.ts.eta +31 -0
  77. package/tsconfig.tsbuildinfo +1 -1
  78. package/buildinschemas copy/autoincreament.autoinc.jsonschema.json +0 -39
  79. package/buildinschemas copy/branch.branch.jsonschema.json +0 -41
  80. package/buildinschemas copy/docnoformat.docno.jsonschema.json +0 -23
  81. package/buildinschemas copy/organization.org.jsonschema.json +0 -50
  82. package/buildinschemas copy/permission.perm.jsonschema.json +0 -23
  83. package/buildinschemas copy/permission.perm.jsonschema.try.json +0 -25
  84. package/buildinschemas copy/tenant.tenant.jsonschema.json +0 -21
  85. package/buildinschemas copy/tenant.tenant.jsonschema.try.json +0 -27
  86. package/buildinschemas copy/user.user.jsonschema.json +0 -31
  87. package/src/processors/jsonschemabuilder.ts-old +0 -383
  88. package/templates/nest/src/simpleapp/workflow/appDelegate.ts.eta +0 -194
  89. package/templates/nest/src/simpleapp/workflow/configuration.ts.eta +0 -46
  90. package/templates/nest/src/simpleapp/workflow/delegates/customer.ts._eta +0 -8
  91. package/templates/nest/src/simpleapp/workflow/delegates/hello.ts._eta +0 -5
  92. package/templates/nest/src/simpleapp/workflow/delegates/index.ts._eta +0 -5
  93. package/templates/nest/src/simpleapp/workflow/delegates/invoice.delegates.ts._eta +0 -9
  94. package/templates/nest/src/simpleapp/workflow/delegates/usertask.ts._eta +0 -3
  95. package/templates/nest/src/simpleapp/workflow/formschema/SimpleApproveReject.ts.eta +0 -8
  96. package/templates/nest/src/simpleapp/workflow/formschema/index.ts.eta +0 -1
@@ -4,13 +4,21 @@
4
4
  * last change 2023-10-28
5
5
  * Author: Ks Tan
6
6
  */
7
- import { Injectable, Logger,Inject } from '@nestjs/common';
7
+ import { Injectable, Logger, Inject } from '@nestjs/common';
8
8
  import { InjectModel } from '@nestjs/mongoose';
9
- import jsonpath from 'jsonpath'
10
- import { uniq } from 'lodash';
9
+ import jsonpath from 'jsonpath';
10
+ import { uniq } from 'lodash';
11
11
  import { AuditTrail } from '../commons/audittrail.service';
12
- import {foreignkeys} from '../commons/dicts/foreignkeys'
13
- import { Model,Types,PipelineStage,mongo, FilterQuery, ProjectionType} from 'mongoose';
12
+ import { foreignkeys } from '../commons/dicts/foreignkeys';
13
+ import { EventEmitter2 } from '@nestjs/event-emitter';
14
+ import {
15
+ Model,
16
+ Types,
17
+ PipelineStage,
18
+ mongo,
19
+ FilterQuery,
20
+ ProjectionType,
21
+ } from 'mongoose';
14
22
  import Ajv from 'ajv';
15
23
  import addFormats from 'ajv-formats';
16
24
  import addErrors from 'ajv-errors';
@@ -50,8 +58,12 @@ export type MoreProjectionType = {
50
58
  };
51
59
  @Injectable()
52
60
  export class SimpleAppService<T extends { _id?: string }> {
61
+
62
+ @Inject(EventEmitter2)
63
+ protected eventEmitter: EventEmitter2
64
+
53
65
  protected logger = new Logger();
54
- protected strictIsolation = true
66
+ protected strictIsolation = true;
55
67
  protected jsonschema = { type: 'object', properties: {}, required: [] };
56
68
  protected documentIdentityCode = 'code';
57
69
  protected documentIdentityLabel = 'label';
@@ -64,15 +76,14 @@ export class SimpleAppService<T extends { _id?: string }> {
64
76
  protected data: T = { _id: '' } as T;
65
77
  protected doc: Model<T>; //set private to prevent developer break data isolation control
66
78
  protected errorlist = [];
67
- protected withDocNumberFormat=false
68
- protected foreignkeys = {}
69
-
79
+ protected withDocNumberFormat = false;
80
+ protected foreignkeys = {};
81
+
70
82
  @Inject(AuditTrail)
71
- protected audittrail: AuditTrail
83
+ protected audittrail: AuditTrail;
72
84
 
73
85
  @Inject(DocNumberFormatGenerator)
74
- protected docnogenerator: DocNumberFormatGenerator
75
-
86
+ protected docnogenerator: DocNumberFormatGenerator;
76
87
 
77
88
  // protected userprovider = new UserContext() ;
78
89
 
@@ -80,14 +91,14 @@ export class SimpleAppService<T extends { _id?: string }> {
80
91
  doctype: string,
81
92
  docname: string,
82
93
  newdoc: Model<T>,
83
- isolationtype: IsolationType = IsolationType.org
84
- ) {
94
+ isolationtype: IsolationType = IsolationType.org,
95
+ ) {
85
96
  // console.log("-------init simpleapp service abstract class -------userprovider=",typeof this.userprovider)
86
97
  this.documentType = doctype.toUpperCase();
87
98
  this.documentName = docname;
88
99
  this.doc = newdoc;
89
100
  this.isolationtype = isolationtype;
90
- this.hook(undefined,HookType.init, undefined);
101
+ this.hook(undefined, HookType.init, undefined);
91
102
  // this.tenantdoc = tenantdoc
92
103
  }
93
104
  getDocumentType = () => this.documentType;
@@ -104,8 +115,8 @@ export class SimpleAppService<T extends { _id?: string }> {
104
115
  this.data = { ...newdata };
105
116
  return this;
106
117
  };
107
- reCalculateValue(data:T) {}
108
- getIsolationFilter = (appuser:UserContext,) => {
118
+ reCalculateValue(data: T) {}
119
+ getIsolationFilter = (appuser: UserContext) => {
109
120
  let isolationFilter = {};
110
121
  switch (this.isolationtype) {
111
122
  case 'none':
@@ -124,7 +135,7 @@ export class SimpleAppService<T extends { _id?: string }> {
124
135
  }
125
136
  return isolationFilter;
126
137
  };
127
- async list(appuser:UserContext,) {
138
+ async list(appuser: UserContext) {
128
139
  try {
129
140
  //console.log("this.isolationFilter",this.getIsolationFilter())
130
141
  const products = await this.doc.find(this.getIsolationFilter(appuser));
@@ -147,7 +158,7 @@ export class SimpleAppService<T extends { _id?: string }> {
147
158
  }
148
159
  // console.log(this.moreAutoCompleteField);
149
160
  };
150
- async getAutoComplete(appuser:UserContext, keyword: string) {
161
+ async getAutoComplete(appuser: UserContext, keyword: string) {
151
162
  try {
152
163
  const filter1 = {};
153
164
  const filter2 = {};
@@ -186,18 +197,18 @@ export class SimpleAppService<T extends { _id?: string }> {
186
197
 
187
198
  /**
188
199
  * Special search function which can by pass data isolation. reserved and try not use
189
- * @param appuser
190
- * @param filters
191
- * @returns
200
+ * @param appuser
201
+ * @param filters
202
+ * @returns
192
203
  */
193
- private async searchNoIsolation(appuser:UserContext,filters: Object) {
204
+ private async searchNoIsolation(appuser: UserContext, filters: Object) {
194
205
  try {
195
- await this.hook(appuser,HookType.beforeSearch, filters);
206
+ await this.hook(appuser, HookType.beforeSearch, filters);
196
207
  const products = await this.doc.find(filters);
197
208
  const productlist = products.map((p: T) => {
198
209
  return p;
199
210
  });
200
- await this.hook(appuser,HookType.afterSearch, productlist);
211
+ await this.hook(appuser, HookType.afterSearch, productlist);
201
212
  // console.log(products);
202
213
  return productlist;
203
214
  } catch (err) {
@@ -205,43 +216,48 @@ export class SimpleAppService<T extends { _id?: string }> {
205
216
  }
206
217
  // return this;
207
218
  }
208
- async aggregate(appuser:UserContext,pipeline:PipelineStage[]){
209
-
210
- if(pipeline[0] && pipeline[0]['$match']){
211
- try{
212
- const isolationFilter= {... this.getIsolationFilter(appuser)}
213
- this.polishIsolationFilter(isolationFilter)
214
-
215
-
216
- Object.assign(pipeline[0]['$match'],isolationFilter);
219
+ async aggregate(appuser: UserContext, pipeline: PipelineStage[]) {
220
+ if (pipeline[0] && pipeline[0]['$match']) {
221
+ try {
222
+ const isolationFilter = { ...this.getIsolationFilter(appuser) };
223
+ this.polishIsolationFilter(isolationFilter);
224
+
225
+ Object.assign(pipeline[0]['$match'], isolationFilter);
217
226
  //console.log("final agg",pipeline)
218
- return await this.doc.aggregate(pipeline)
219
- }catch(err){
220
- throw new InternalServerErrorException(err);
227
+ return await this.doc.aggregate(pipeline);
228
+ } catch (err) {
229
+ throw new InternalServerErrorException(err);
221
230
  }
222
-
223
- }else{
224
- throw new InternalServerErrorException('first aggregate pipelinestage shall use $match');
231
+ } else {
232
+ throw new InternalServerErrorException(
233
+ 'first aggregate pipelinestage shall use $match',
234
+ );
225
235
  }
226
-
227
236
  }
228
- async search(appuser:UserContext,filters: FilterQuery<T>,projection:ProjectionType<T>=undefined,sort:any=undefined) {
237
+ async search(
238
+ appuser: UserContext,
239
+ filters: FilterQuery<T>,
240
+ projection: ProjectionType<T> = undefined,
241
+ sort: any = undefined,
242
+ ) {
229
243
  try {
230
- const isolationFilter= {... this.getIsolationFilter(appuser)}
231
- this.polishIsolationFilter(isolationFilter)
232
-
244
+ const isolationFilter = { ...this.getIsolationFilter(appuser) };
245
+ this.polishIsolationFilter(isolationFilter);
246
+
233
247
  // console.log("initial search",filters)
234
- const newfilters ={...filters,...isolationFilter}
235
- await this.hook(appuser,HookType.beforeSearch, newfilters);
248
+ const newfilters = { ...filters, ...isolationFilter };
249
+ await this.hook(appuser, HookType.beforeSearch, newfilters);
236
250
  // console.log("before _find",newfilters)
237
251
  // console.log("this.doc",this.doc)
238
- const products = await this.doc.find(newfilters,projection,{session:appuser.getDBSession()}).sort(sort);
252
+ const products = await this.doc
253
+ .find(newfilters, projection, { session: appuser.getDBSession() })
254
+ .sort(sort);
239
255
  // console.log("after search",products)
240
256
  const productlist = products.map((p: T) => {
241
257
  return p;
242
258
  });
243
259
  // console.log("after map",productlist)
244
- await this.hook(appuser,HookType.afterSearch, productlist);
260
+ await this.hook(appuser, HookType.afterSearch, productlist);
245
261
  // console.log(products);
246
262
  return productlist;
247
263
  } catch (err) {
@@ -249,10 +265,10 @@ export class SimpleAppService<T extends { _id?: string }> {
249
265
  }
250
266
  // return this;
251
267
  }
252
- async findById(appuser:UserContext,id: string) {
253
- await this.hook(appuser,HookType.beforeFetchRecord, id);
254
- const data = await this.search(appuser,{ _id: id });
255
- await this.hook(appuser,HookType.afterFetchRecord, data);
268
+ async findById(appuser: UserContext, id: string) {
269
+ await this.hook(appuser, HookType.beforeFetchRecord, id);
270
+ const data = await this.search(appuser, { _id: id });
271
+ await this.hook(appuser, HookType.afterFetchRecord, data);
256
272
  if (data.length == 1) {
257
273
  // console.log('data0', data[0]);
258
274
  return data[0];
@@ -261,88 +277,100 @@ export class SimpleAppService<T extends { _id?: string }> {
261
277
  }
262
278
  }
263
279
 
264
-
265
- async create(appuser:UserContext, data:T) {
266
-
280
+ async create(appuser: UserContext, data: T) {
267
281
  let result;
268
- if(!data._id){
269
- data._id = crypto.randomUUID()
282
+ if (!data._id) {
283
+ data._id = crypto.randomUUID();
270
284
  }
271
- const dbsession = appuser.getDBSession()
272
- if(!dbsession.inTransaction()){
273
- dbsession.startTransaction()
285
+ const dbsession = appuser.getDBSession();
286
+ if (!dbsession.inTransaction()) {
287
+ dbsession.startTransaction();
274
288
  }
275
289
 
276
-
277
- this.logger.debug('this.withDocNumberFormat :' + this.withDocNumberFormat + ' && ' + '!data[this.documentIdentityCode] ==' + !data[this.documentIdentityCode])
278
- if(this.withDocNumberFormat && !data[this.documentIdentityCode]){
279
- await this.genNewDocNo(appuser,data)
290
+ this.logger.debug(
291
+ 'this.withDocNumberFormat :' +
292
+ this.withDocNumberFormat +
293
+ ' && ' +
294
+ '!data[this.documentIdentityCode] ==' +
295
+ !data[this.documentIdentityCode],
296
+ );
297
+ if (this.withDocNumberFormat && !data[this.documentIdentityCode]) {
298
+ await this.genNewDocNo(appuser, data);
280
299
  }
281
-
282
- await this.hook(appuser,HookType.beforeCreate, data);
283
-
284
- let isolationFilter:any = {...appuser.getCreateFilter()}
285
- isolationFilter = this.polishIsolationFilter(isolationFilter,data)
286
-
287
- this.logger.debug("isolationFilter",'SimpleAppService')
288
- this.logger.debug(isolationFilter,'SimpleAppService')
289
- this.logger.debug("Create data before isolation",'SimpleAppService')
290
- this.logger.debug(data,'SimpleAppService')
300
+
301
+ await this.hook(appuser, HookType.beforeCreate, data);
302
+
303
+ let isolationFilter: any = { ...appuser.getCreateFilter() };
304
+ isolationFilter = this.polishIsolationFilter(isolationFilter, data);
305
+
306
+ this.logger.debug('isolationFilter', 'SimpleAppService');
307
+ this.logger.debug(isolationFilter, 'SimpleAppService');
308
+ this.logger.debug('Create data before isolation', 'SimpleAppService');
309
+ this.logger.debug(data, 'SimpleAppService');
291
310
  Object.assign(data, isolationFilter);
292
- this.reCalculateValue(data)
293
- await this.validateData(appuser,data);
294
- this.logger.debug("Create record",'SimpleAppService')
295
- this.logger.debug(data,'SimpleAppService')
311
+ this.reCalculateValue(data);
312
+ await this.validateData(appuser, data);
313
+ this.logger.debug('Create record', 'SimpleAppService');
314
+ this.logger.debug(data, 'SimpleAppService');
296
315
 
297
- this.applyNestedDateTime(appuser,data,'create')
316
+ this.applyNestedDateTime(appuser, data, 'create');
298
317
  const newdoc = new this.doc(data);
299
- await this.identifyForeignKeys(appuser,data)
300
- try{
301
- result = await newdoc.save({session:dbsession})
302
- appuser.addInsertedRecordId(this.documentName, result._id)
303
- }catch(err){
304
- this.logger.error(err)
305
- throw new InternalServerErrorException(err)
318
+ await this.identifyForeignKeys(appuser, data);
319
+ try {
320
+ result = await newdoc.save({ session: dbsession });
321
+ appuser.addInsertedRecordId(this.documentName, result._id);
322
+ } catch (err) {
323
+ this.logger.error(err);
324
+ throw new InternalServerErrorException(err);
306
325
  }
307
-
308
326
 
309
-
310
327
  // this.doc.create(data)
311
328
  // this.doc
312
329
 
313
330
  // result = await newdoc.save()
314
- await this.hook(appuser,HookType.afterCreate, result);
331
+ await this.hook(appuser, HookType.afterCreate, result);
315
332
 
316
333
  return result as T;
317
334
  }
318
-
319
335
 
320
- applyNestedDateTime=(appuser:UserContext,data:any,transtype:string)=>{
321
-
322
- const props = Object.getOwnPropertyNames(data)
323
- for(let i=0;i<props.length;i++){
324
- const key = props[i]
325
- //need to apply nested
326
- if(Array.isArray(data[key]) && data[key].length>0 && typeof data[key][0] == 'object'){
327
- for(let j=0;j<data[key].length;j++){
328
- this.applyNestedDateTime(appuser,data[key][j],transtype)
336
+ applyNestedDateTime = (
337
+ appuser: UserContext,
338
+ data: any,
339
+ transtype: string,
340
+ ) => {
341
+ const props = Object.getOwnPropertyNames(data);
342
+ for (let i = 0; i < props.length; i++) {
343
+ const key = props[i];
344
+ //need to apply nested
345
+ if (
346
+ Array.isArray(data[key]) &&
347
+ data[key].length > 0 &&
348
+ typeof data[key][0] == 'object'
349
+ ) {
350
+ for (let j = 0; j < data[key].length; j++) {
351
+ this.applyNestedDateTime(appuser, data[key][j], transtype);
329
352
  }
330
- }else if(key == 'created'){
331
- data['created'] = (transtype == 'create' || !data['created']) ? new Date() .toISOString() : data['created']
332
- }else if(key == 'createdBy'){
333
- data['createdBy'] = (transtype == 'create' || !data['createdBy']) ? appuser.getUid() : data['createdBy']
334
- }else if(key == 'updated'){
335
- data['updated'] = new Date() .toISOString()
336
- }else if(key == 'updatedBy'){
337
- data['updatedBy'] = appuser.getUid()
338
- }
353
+ } else if (key == 'created') {
354
+ data['created'] =
355
+ transtype == 'create' || !data['created']
356
+ ? new Date().toISOString()
357
+ : data['created'];
358
+ } else if (key == 'createdBy') {
359
+ data['createdBy'] =
360
+ transtype == 'create' || !data['createdBy']
361
+ ? appuser.getUid()
362
+ : data['createdBy'];
363
+ } else if (key == 'updated') {
364
+ data['updated'] = new Date().toISOString();
365
+ } else if (key == 'updatedBy') {
366
+ data['updatedBy'] = appuser.getUid();
367
+ }
339
368
  }
340
-
341
- }
342
- hook = async (appuser:UserContext,type: string, data?: any) => {
369
+ };
370
+ hook = async (appuser: UserContext, type: string, data?: any) => {
343
371
  return true;
344
372
  };
345
- async validateData(appuser:UserContext,data: T) {
373
+ async validateData(appuser: UserContext, data: T) {
346
374
  const ajv = new Ajv({ allErrors: true, useDefaults: true });
347
375
  addFormats(ajv);
348
376
  addErrors(ajv);
@@ -351,19 +379,21 @@ export class SimpleAppService<T extends { _id?: string }> {
351
379
  ajv.addFormat('text', /.*$/);
352
380
  ajv.addFormat('html', /.*$/);
353
381
  ajv.addFormat('documentno', /.*$/);
354
-
355
382
 
356
383
  ajv.addKeyword({ keyword: 'x-foreignkey', schemaType: 'string' });
357
- ajv.addKeyword({ keyword: 'x-simpleapp-config', schemaType: 'object' });
384
+ ajv.addKeyword({ keyword: 'x-simpleapp-config', schemaType: 'object' });
358
385
 
359
- const issuccess = await this.hook(appuser,HookType.beforeValidation, data);
386
+ const issuccess = await this.hook(appuser, HookType.beforeValidation, data);
360
387
  if (!issuccess) {
361
388
  const errormsg: string[] = [];
362
389
  for (let i = 0; i < this.errorlist.length; i++) {
363
390
  errormsg.push(this.errorlist[i].message);
364
391
  }
365
392
  this.logger.log('run hook during validation');
366
- throw new BadRequestException("Before validation hook failed",errormsg as HttpExceptionOptions);
393
+ throw new BadRequestException(
394
+ 'Before validation hook failed',
395
+ errormsg as HttpExceptionOptions,
396
+ );
367
397
  }
368
398
 
369
399
  let validate;
@@ -373,81 +403,87 @@ export class SimpleAppService<T extends { _id?: string }> {
373
403
  this.logger.error('compile error', err);
374
404
  throw new ForbiddenException(err.message);
375
405
  }
376
- const valid = validate(data);
377
- if (!valid) {
378
- this.logger.error(JSON.stringify(validate.errors), 'validate errors:');
379
- throw new BadRequestException("Data validation failed",validate.errors as HttpExceptionOptions);
380
- }
381
- await this.hook(appuser,HookType.afterValidation, data);
382
-
406
+ const valid = validate(data);
407
+ if (!valid) {
408
+ this.logger.error(JSON.stringify(validate.errors), 'validate errors:');
409
+ throw new BadRequestException(
410
+ 'Data validation failed',
411
+ validate.errors as HttpExceptionOptions,
412
+ );
413
+ }
414
+ await this.hook(appuser, HookType.afterValidation, data);
383
415
  }
384
416
 
385
- polishIsolationFilter = (filterIsolation:any,data:any={}) =>{
386
- if(this.isolationtype == 'none'){
387
- delete filterIsolation['branchId']
388
- delete filterIsolation['orgId']
389
- delete filterIsolation['tenantId']
417
+ polishIsolationFilter = (filterIsolation: any, data: any = {}) => {
418
+ if (this.isolationtype == 'none') {
419
+ delete filterIsolation['branchId'];
420
+ delete filterIsolation['orgId'];
421
+ delete filterIsolation['tenantId'];
390
422
  }
391
- if(this.isolationtype == 'tenant' && !this.strictIsolation){
423
+ if (this.isolationtype == 'tenant' && !this.strictIsolation) {
392
424
  // delete filterIsolation['tenantId']
393
- if(data['tenantId']){
394
- filterIsolation['tenantId']=data['tenantId']
425
+ if (data['tenantId']) {
426
+ filterIsolation['tenantId'] = data['tenantId'];
395
427
  }
396
- delete filterIsolation['branchId']
397
- delete filterIsolation['orgId']
428
+ delete filterIsolation['branchId'];
429
+ delete filterIsolation['orgId'];
398
430
  }
399
- if(this.isolationtype == 'org' && !this.strictIsolation){
400
- // delete filterIsolation['tenantId']
401
- if(data['tenantId']){
402
- filterIsolation['tenantId']=data['tenantId']
431
+ if (this.isolationtype == 'org' && !this.strictIsolation) {
432
+ // delete filterIsolation['tenantId']
433
+ if (data['tenantId']) {
434
+ filterIsolation['tenantId'] = data['tenantId'];
403
435
  }
404
- if(data['orgId']){
405
- filterIsolation['orgId']=data['orgId']
436
+ if (data['orgId']) {
437
+ filterIsolation['orgId'] = data['orgId'];
406
438
  }
407
- // delete filterIsolation['orgId']
408
- delete filterIsolation['branchId']
439
+ // delete filterIsolation['orgId']
440
+ delete filterIsolation['branchId'];
409
441
  }
410
- return filterIsolation
411
- }
412
- async findIdThenDelete(appuser:UserContext,id: string): Promise<any> {
413
- const deletedata = await this.findById(appuser,id);
414
- if(!deletedata){
415
- throw new NotFoundException(`${id} not found`,"not found")
442
+ return filterIsolation;
443
+ };
444
+ async findIdThenDelete(appuser: UserContext, id: string): Promise<any> {
445
+ const deletedata = await this.findById(appuser, id);
446
+ if (!deletedata) {
447
+ throw new NotFoundException(`${id} not found`, 'not found');
416
448
  }
417
- const dbsession = appuser.getDBSession()
418
- if(!dbsession.inTransaction()){
419
- dbsession.startTransaction()
449
+ const dbsession = appuser.getDBSession();
450
+ if (!dbsession.inTransaction()) {
451
+ dbsession.startTransaction();
420
452
  }
421
453
 
422
- let dependency
454
+ let dependency;
423
455
  try {
424
- await this.hook(appuser,HookType.beforeDelete, id);
425
- this.logger.debug('delete record',this.documentName, id);
456
+ await this.hook(appuser, HookType.beforeDelete, id);
457
+ this.logger.debug('delete record', this.documentName, id);
426
458
  dependency = await this.getRelatedRecords(id);
427
459
  //console.log('dependency', dependency);
428
460
  if (!dependency) {
429
461
  let filterIsolation = this.getIsolationFilter(appuser);
430
- this.polishIsolationFilter(filterIsolation)
431
-
432
- filterIsolation['_id'] = id;
433
- this.logger.debug('delete filter', filterIsolation);
434
- const result = await this.doc.deleteOne(filterIsolation).session(dbsession);
462
+ this.polishIsolationFilter(filterIsolation);
435
463
 
436
- appuser.addUpdatedRecordId(this.documentName, id)
437
- const deleteresult = {result:result, data: deletedata}
438
- this.logger.debug(deleteresult, " delete result" +this.doc.collection.name,)
464
+ filterIsolation['_id'] = id;
465
+ this.logger.debug('delete filter', filterIsolation);
466
+ const result = await this.doc
467
+ .deleteOne(filterIsolation)
468
+ .session(dbsession);
469
+
470
+ appuser.addUpdatedRecordId(this.documentName, id);
471
+ const deleteresult = { result: result, data: deletedata };
472
+ this.logger.debug(
473
+ deleteresult,
474
+ ' delete result' + this.doc.collection.name,
475
+ );
439
476
  // this.doc.findByIdAndDelete(id)
440
- await this.hook(appuser,HookType.afterDelete, deleteresult);
441
-
477
+ await this.hook(appuser, HookType.afterDelete, deleteresult);
478
+
442
479
  //this.doc.findByIdAndDelete(id);
443
480
  return deleteresult;
444
481
  } else {
445
482
  //console.log("reject query",dependency)
446
-
447
- throw new ForbiddenException('Foreignkey constraint',dependency)
483
+
484
+ throw new ForbiddenException('Foreignkey constraint', dependency);
448
485
  }
449
486
  } catch (err) {
450
-
451
487
  throw new InternalServerErrorException(err);
452
488
  //JSON.stringify(dependency),JSON.stringify(dependency)
453
489
  }
@@ -457,38 +493,45 @@ export class SimpleAppService<T extends { _id?: string }> {
457
493
  // this.doc.updateOne(data);
458
494
  // };
459
495
 
460
- findIdThenUpdate = async (appuser:UserContext,id: string, data: T,session:mongo.ClientSession=undefined) => {
461
- const existingdata = await this.findById(appuser,id);
462
- if(!existingdata){
463
- throw new NotFoundException(`${id} not found`,"not found")
496
+ findIdThenUpdate = async (
497
+ appuser: UserContext,
498
+ id: string,
499
+ data: T,
500
+ session: mongo.ClientSession = undefined,
501
+ ) => {
502
+ const existingdata = await this.findById(appuser, id);
503
+ if (!existingdata) {
504
+ throw new NotFoundException(`${id} not found`, 'not found');
464
505
  }
465
- await this.hook(appuser,HookType.beforeUpdate, data);
506
+ await this.hook(appuser, HookType.beforeUpdate, data);
466
507
 
467
- const dbsession = appuser.getDBSession()
468
- if(!dbsession.inTransaction()){
469
- dbsession.startTransaction()
508
+ const dbsession = appuser.getDBSession();
509
+ if (!dbsession.inTransaction()) {
510
+ dbsession.startTransaction();
470
511
  }
471
512
 
472
513
  // try {
473
514
  Object.assign(data, appuser.getUpdateFilter());
474
515
  Object.assign(existingdata, data);
475
- delete data['_id']
516
+ delete data['_id'];
476
517
 
477
- this.reCalculateValue(data)
518
+ this.reCalculateValue(data);
478
519
  // existingdata['_id']=''
479
520
  // console.log("newdata",data)
480
- await this.validateData(appuser,data);
481
-
482
- const isolationFilter = {...this.getIsolationFilter(appuser)}
483
- this.polishIsolationFilter(isolationFilter)
521
+ await this.validateData(appuser, data);
522
+
523
+ const isolationFilter = { ...this.getIsolationFilter(appuser) };
524
+ this.polishIsolationFilter(isolationFilter);
484
525
 
485
526
  isolationFilter['_id'] = id;
486
- this.applyNestedDateTime(appuser,data,'update')
487
- try{
488
- const result = await this.doc.findOneAndUpdate(isolationFilter, data).session(dbsession);
489
- appuser.addUpdatedRecordId(this.documentName, data._id)
490
- await this.hook(appuser,HookType.afterUpdate, data);
491
- return result;
527
+ this.applyNestedDateTime(appuser, data, 'update');
528
+ try {
529
+ const result = await this.doc
530
+ .findOneAndUpdate(isolationFilter, data)
531
+ .session(dbsession);
532
+ appuser.addUpdatedRecordId(this.documentName, data._id);
533
+ await this.hook(appuser, HookType.afterUpdate, data);
534
+ return result;
492
535
  } catch (err) {
493
536
  throw new InternalServerErrorException(err.message);
494
537
  }
@@ -496,20 +539,19 @@ export class SimpleAppService<T extends { _id?: string }> {
496
539
 
497
540
  //find what foreign key constraint
498
541
  async getRelatedRecords(id: string) {
499
- this.logger.debug("get foreignkey for delete:",id)
500
-
501
-
502
- if(foreignkeys === undefined){
503
- this.logger.error("foreignkeys object undetected")
504
- throw new InternalServerErrorException("foreignkeys object undetected")
542
+ this.logger.debug('get foreignkey for delete:', id);
543
+
544
+ if (foreignkeys === undefined) {
545
+ this.logger.error('foreignkeys object undetected');
546
+ throw new InternalServerErrorException('foreignkeys object undetected');
505
547
  }
506
-
548
+
507
549
  const foreignkeysettings = foreignkeys[this.documentName];
508
- if(!foreignkeysettings){
509
- return null
550
+ if (!foreignkeysettings) {
551
+ return null;
510
552
  }
511
553
  const propkeys = Object.getOwnPropertyNames(foreignkeysettings);
512
-
554
+
513
555
  if (propkeys.length > 0) {
514
556
  //console.log('Have properties');
515
557
  for (let i = 0; i < propkeys.length; i++) {
@@ -519,14 +561,13 @@ export class SimpleAppService<T extends { _id?: string }> {
519
561
  const collection = this.doc.db.collection(collectionname);
520
562
  //single schema may have multiple foreign key link here, loop all
521
563
  for (let j = 0; j < fobjs.length; j++) {
522
- const fkey = fobjs[j]
523
- let filter = {}
564
+ const fkey = fobjs[j];
565
+ let filter = {};
524
566
  filter[fkey] = id;
525
-
526
-
527
- const result = await collection.findOne(filter);
567
+
568
+ const result = await collection.findOne(filter);
528
569
  if (result) {
529
- this.logger.error(result,"related result found")
570
+ this.logger.error(result, 'related result found');
530
571
  return result;
531
572
  }
532
573
  }
@@ -547,109 +588,130 @@ export class SimpleAppService<T extends { _id?: string }> {
547
588
  * @param docstatus
548
589
  * @returns Promise
549
590
  */
550
- async setDocumentStatus(appuser:UserContext,id: string, docstatus: string) {
591
+ async setDocumentStatus(appuser: UserContext, id: string, docstatus: string) {
551
592
  const partialdata: T = {} as T;
552
593
  partialdata['documentStatus'] = docstatus;
553
- return this.findIdThenUpdate(appuser,id, partialdata);
594
+ return this.findIdThenUpdate(appuser, id, partialdata);
554
595
  }
555
596
 
556
- async genNewDocNo(appuser:UserContext,data:T){
557
- this.logger.debug("genNewDocNo")
558
- const result = await this.docnogenerator.generateNextNumberFromDocument(appuser,this.documentType,data)
559
- this.logger.debug(result,"genNewDocNo")
560
- data[this.documentIdentityCode]=result
597
+ startWorkflow(appuser:UserContext,processName:string,workflowData:any){
598
+ return this.eventEmitter.emit('workflow.start',appuser,'suspendcustomer',workflowData)
561
599
  }
562
600
 
601
+ async genNewDocNo(appuser: UserContext, data: T) {
602
+ this.logger.debug('genNewDocNo');
603
+ const result = await this.docnogenerator.generateNextNumberFromDocument(
604
+ appuser,
605
+ this.documentType,
606
+ data,
607
+ );
608
+ this.logger.debug(result, 'genNewDocNo');
609
+ data[this.documentIdentityCode] = result;
610
+ }
563
611
 
564
- async identifyForeignKeys(appuser:UserContext,data:T){
565
- /**
612
+ async identifyForeignKeys(appuser: UserContext, data: T) {
613
+ /**
566
614
  * 1. looping schemas identify what foreign key exists
567
- * 2. loop through record obtain all foreign key value
615
+ * 2. loop through record obtain all foreign key value
568
616
  * 3. get all unique key value in array {product:['xxxx','yyyy'],customer:['aaa']}
569
617
  */
570
- const schema = this.jsonschema
571
-
572
- //get all foreign keys catalogue
573
- const collections = Object.getOwnPropertyNames(this.foreignkeys)
574
-
575
- //obtain exists data in according foreign key
576
- const pipelines :PipelineStage[] =[ {$match:{_id:false}}] //exclude data from current collection
577
- const vdata = data//['_doc']
578
- const keystore = {}
579
- collections.forEach((collectionname)=>{
580
- const fks:string[] = this.foreignkeys[collectionname]
581
- let results:string[] = []
582
- fks.forEach(fieldpath=>{
618
+ const schema = this.jsonschema;
619
+
620
+ //get all foreign keys catalogue
621
+ const collections = Object.getOwnPropertyNames(this.foreignkeys);
622
+
623
+ //obtain exists data in according foreign key
624
+ const pipelines: PipelineStage[] = [{ $match: { _id: false } }]; //exclude data from current collection
625
+ const vdata = data; //['_doc']
626
+ const keystore = {};
627
+ collections.forEach((collectionname) => {
628
+ const fks: string[] = this.foreignkeys[collectionname];
629
+ let results: string[] = [];
630
+ fks.forEach((fieldpath) => {
583
631
  //console.log("fieldpath:",fieldpath,"vdata",data,vdata)
584
- const tmp = jsonpath.query(vdata,fieldpath).filter((item:string)=>item!='')
632
+ const tmp = jsonpath
633
+ .query(vdata, fieldpath)
634
+ .filter((item: string) => item != '');
585
635
  // console.log("tmp",tmp)
586
-
587
- results = results.concat(tmp)
588
- })
589
-
590
-
591
-
592
- if(results.length>0){
593
- if(results.length>1){
594
- results = uniq<string>(results)
595
- }
596
-
597
- keystore[collectionname]= results
598
- //console.log("keystorekeystore",keystore)
599
- let addfield={$addFields:{collection:collectionname}}
600
-
601
- const stagefilter:PipelineStage = {
602
- $unionWith: {coll:collectionname,pipeline: [{ $match: { _id: {$in:results}} },addfield,{$project:{collection:1}}, ]}
603
- }
604
- pipelines.push(stagefilter)
605
- }
606
- })
607
- this.logger.log(pipelines,'identifyForeignKeys pipelines')
608
- // this.doc.db.collection(collectionname);
609
- const unionresult = await this.doc.aggregate(pipelines)
610
-
611
- if(!unionresult){
612
- this.logger.error("foreign key control failed ",'identifyForeignKeys')
613
- throw new InternalServerErrorException("Foreignkey check execution error",pipelines as HttpExceptionOptions)
614
- }else{
615
- let searchresult:any = {}
616
- unionresult.forEach(item=>{
617
- if(searchresult[item.collection]){
618
- searchresult[item.collection].push(item._id)
619
- }else{
620
- searchresult[item.collection] = [item._id]
621
- }
622
- })
623
- this.logger.log(unionresult,this.documentType+" search Result")
624
-
625
- //search is it all foreign key exists in db
626
- for(let i=0; i<collections.length; i++){
627
- const collectionname = collections[i]
628
- const keys:string[] = keystore[collectionname]
629
- if(!keys){
630
- continue
631
- }
632
- for(let k=0;k<keys.length; k++){
633
- const key = keys[k]
634
- if(searchresult[collectionname] && searchresult[collectionname].includes(key)){
635
- this.logger.debug(`foreignkey ${collectionname}->${key} exists`)
636
- }
637
- else if(appuser.searchInsertedRecordId(collectionname,key)){
638
- this.logger.debug(`foreignkey ${collectionname} exists in user context which not yet commited`)
639
- }
640
- else{
641
- this.logger.warn(`${this.documentType}: Foreignkey ${key} at collection ${collectionname} does not exist`,'identifyForeignKeys')
642
- this.logger.log(appuser.getModifieds,"appuser.getModifieds")
643
- const errordata = {key:key,collection:collectionname}
644
- throw new BadRequestException(`${this.documentType}: Foreignkey ${key} at collection ${collectionname} does not exist`,JSON.stringify(errordata))
645
-
646
- }
647
- }
648
-
649
- }
650
-
651
- }
652
- }
653
636
 
637
+ results = results.concat(tmp);
638
+ });
639
+
640
+ if (results.length > 0) {
641
+ if (results.length > 1) {
642
+ results = uniq<string>(results);
643
+ }
654
644
 
645
+ keystore[collectionname] = results;
646
+ //console.log("keystorekeystore",keystore)
647
+ let addfield = { $addFields: { collection: collectionname } };
648
+
649
+ const stagefilter: PipelineStage = {
650
+ $unionWith: {
651
+ coll: collectionname,
652
+ pipeline: [
653
+ { $match: { _id: { $in: results } } },
654
+ addfield,
655
+ { $project: { collection: 1 } },
656
+ ],
657
+ },
658
+ };
659
+ pipelines.push(stagefilter);
660
+ }
661
+ });
662
+ this.logger.log(pipelines, 'identifyForeignKeys pipelines');
663
+ // this.doc.db.collection(collectionname);
664
+ const unionresult = await this.doc.aggregate(pipelines);
665
+
666
+ if (!unionresult) {
667
+ this.logger.error('foreign key control failed ', 'identifyForeignKeys');
668
+ throw new InternalServerErrorException(
669
+ 'Foreignkey check execution error',
670
+ pipelines as HttpExceptionOptions,
671
+ );
672
+ } else {
673
+ let searchresult: any = {};
674
+ unionresult.forEach((item) => {
675
+ if (searchresult[item.collection]) {
676
+ searchresult[item.collection].push(item._id);
677
+ } else {
678
+ searchresult[item.collection] = [item._id];
679
+ }
680
+ });
681
+ this.logger.log(unionresult, this.documentType + ' search Result');
682
+
683
+ //search is it all foreign key exists in db
684
+ for (let i = 0; i < collections.length; i++) {
685
+ const collectionname = collections[i];
686
+ const keys: string[] = keystore[collectionname];
687
+ if (!keys) {
688
+ continue;
689
+ }
690
+ for (let k = 0; k < keys.length; k++) {
691
+ const key = keys[k];
692
+ if (
693
+ searchresult[collectionname] &&
694
+ searchresult[collectionname].includes(key)
695
+ ) {
696
+ this.logger.debug(`foreignkey ${collectionname}->${key} exists`);
697
+ } else if (appuser.searchInsertedRecordId(collectionname, key)) {
698
+ this.logger.debug(
699
+ `foreignkey ${collectionname} exists in user context which not yet commited`,
700
+ );
701
+ } else {
702
+ this.logger.warn(
703
+ `${this.documentType}: Foreignkey ${key} at collection ${collectionname} does not exist`,
704
+ 'identifyForeignKeys',
705
+ );
706
+ this.logger.log(appuser.getModifieds, 'appuser.getModifieds');
707
+ const errordata = { key: key, collection: collectionname };
708
+ throw new BadRequestException(
709
+ `${this.documentType}: Foreignkey ${key} at collection ${collectionname} does not exist`,
710
+ JSON.stringify(errordata),
711
+ );
712
+ }
713
+ }
714
+ }
715
+ }
716
+ }
655
717
  }