@simitgroup/simpleapp-generator 2.0.2-f-alpha → 2.0.2-h-alpha

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/ReleaseNote.md CHANGED
@@ -1,3 +1,14 @@
1
+ [2.0.2h-alpha]
2
+
3
+ 1. added error handling event for create,update,delete,setStatus
4
+ 2. improve saga document number generator check max 10 number ahead base on target document's unique key (with data isolation)
5
+
6
+
7
+ [2.0.2g-alpha]
8
+
9
+ 1. fix search no header return pagination
10
+
11
+
1
12
  [2.0.2f-alpha]
2
13
 
3
14
  1. fix mini-app search condition
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simitgroup/simpleapp-generator",
3
- "version": "2.0.2f-alpha",
3
+ "version": "2.0.2h-alpha",
4
4
  "description": "frontend nuxtjs and backend nests code generator using jsonschema",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
@@ -1,7 +1,7 @@
1
1
  import { UserContext } from '../user-context/user.context';
2
2
  import { InjectModel } from '@nestjs/mongoose';
3
3
  import { Model, FilterQuery } from 'mongoose';
4
- import { DocNumberFormatResult, ForeignKey, SchemaFields } from '../../framework/schemas';
4
+ import { DocNumberFormatResult, ForeignKey, IsolationType, SchemaFields } from '../../framework/schemas';
5
5
  import { DocumentNoFormat, DocumentNoFormatPreview } from './document-no-format.schema';
6
6
  import { Injectable, InternalServerErrorException, BadRequestException } from '@nestjs/common';
7
7
  import { alldocuments } from './document.dict';
@@ -9,6 +9,7 @@ import dayjs from 'dayjs';
9
9
 
10
10
  export class SimpleAppDocumentNoFormatService {
11
11
  constructor(@InjectModel('DocumentNoFormat') private docformat: Model<DocumentNoFormat>) {}
12
+ getDocumentMeta = (docType:string)=> alldocuments.find(x => x.docType === docType)
12
13
 
13
14
  async runListDocFormats(appUser: UserContext, doctype: string) {
14
15
  doctype = doctype.toUpperCase();
@@ -69,13 +70,26 @@ export class SimpleAppDocumentNoFormatService {
69
70
  const result = await this.docformat.find(filter).session(session);
70
71
  if (result && result.length > 0) {
71
72
  const d: DocumentNoFormat = result[0];
73
+ const initNumber = d.nextNumber
72
74
  const recordId = d._id;
73
- const newdocno = SimpleAppDocumentNoFormatService.previewDocNo(d);
74
- const newnextnumber = d.nextNumber + 1;
75
+ // const newdocno = SimpleAppDocumentNoFormatService.previewDocNo(d);
76
+
77
+ const checker = await this.documentNumberChecker(appUser,doctype,d)
78
+
79
+ if(!checker.accept){
80
+ d.nextNumber = initNumber
81
+ const errdocno = SimpleAppDocumentNoFormatService.previewDocNo(d);
82
+ throw new InternalServerErrorException(`Unique Key ${errdocno} conflict`);
83
+ }
84
+
85
+ const newdocno = checker.newDocNo
86
+ const newnextnumber = initNumber + checker.offset;
75
87
  const updatedata = { nextNumber: newnextnumber } as DocumentNoFormat;
76
88
 
77
89
  const options = process.env.BlockGenerateDocNumberWithSession ? {} : { session: session };
78
90
  const updateresult = await this.docformat.findByIdAndUpdate(recordId, updatedata, options);
91
+ result[0].nextNumber = initNumber
92
+ // console.log('add transaction step for update document no',[result[0]])
79
93
  appUser.addTransactionStep('update','documentnoformat',[recordId],[result[0]])
80
94
  if (updateresult) {
81
95
  const result: DocNumberFormatResult = {
@@ -211,17 +225,27 @@ export class SimpleAppDocumentNoFormatService {
211
225
  //
212
226
  Object.assign(filter, appUser.getBranchFilter());
213
227
  const result = await this.docformat.find(filter).session(appUser.getDBSession());
228
+
214
229
  if (result && result.length > 0) {
215
230
  const d: DocumentNoFormat = result[0];
216
- const recordId = d._id;
231
+ const initNumber = d.nextNumber
232
+ const checker = await this.documentNumberChecker(appUser,doctype,d)
217
233
 
218
- for (let i = 0; i < quantity; i++) {
219
- d.nextNumber++;
234
+ if(!checker.accept){
235
+ d.nextNumber = initNumber
236
+ const errdocno = SimpleAppDocumentNoFormatService.previewDocNo(d);
237
+ throw new InternalServerErrorException(`Unique Key ${errdocno} conflict during request-multiple-number`);
238
+ }
239
+
240
+
241
+ d.nextNumber=initNumber + checker.offset - 1 //offset will return >=1, we want start from 0
242
+ const recordId = d._id;
243
+ for (let i = 0; i < quantity; i++) {
220
244
  const newdocno = SimpleAppDocumentNoFormatService.previewDocNo(d);
221
245
  documentNumbers.push(newdocno);
246
+ d.nextNumber++;
222
247
  }
223
- const updatedata = { nextNumber: d.nextNumber + 1 } as DocumentNoFormat;
224
-
248
+ const updatedata = { nextNumber: d.nextNumber } as DocumentNoFormat;
225
249
  const options = process.env.BlockGenerateDocNumberWithSession ? {} : { session: appUser.getDBSession(), };
226
250
  const updateresult = await this.docformat.findByIdAndUpdate(recordId, updatedata, options);
227
251
  appUser.addTransactionStep('update','documentnoformat',[recordId],[result[0]])
@@ -230,4 +254,51 @@ export class SimpleAppDocumentNoFormatService {
230
254
  throw new BadRequestException(`No active document number found for ${doctype}. Please update in Settings > Document Numbering Format`);
231
255
  }
232
256
  }
257
+
258
+ applyIsolationFilter (appUser:UserContext, filter:any, docType:string){
259
+ const docMeta = this.getDocumentMeta(docType)
260
+ if(docMeta.isolationType===IsolationType.tenant) {
261
+ filter['tenantId'] = appUser.tenantId
262
+ }
263
+ if(docMeta.isolationType===IsolationType.org) {
264
+ filter['tenantId'] = appUser.tenantId
265
+ filter['orgId'] = appUser.orgId
266
+ }
267
+ if(docMeta.isolationType===IsolationType.branch) {
268
+ filter['tenantId'] = appUser.tenantId
269
+ filter['orgId'] = appUser.orgId
270
+ filter['branchId'] = appUser.branchId
271
+ }
272
+
273
+ return filter
274
+ }
275
+
276
+
277
+ async documentNumberChecker(appUser:UserContext,docType:string,docFormat:DocumentNoFormat){
278
+
279
+ const docMeta = this.getDocumentMeta(docType)
280
+ const collectionName = docMeta.docName.toLowerCase()
281
+ const collection = this.docformat.db.collection(collectionName);
282
+ const codeField = docMeta.uniqueKey
283
+ let newdocno = ''
284
+ let attempt = 0;
285
+ let accept = false;
286
+ // console.log("run documentNumberChecker")
287
+ for (attempt = 0; attempt < 10; attempt++) {
288
+ newdocno = SimpleAppDocumentNoFormatService.previewDocNo(docFormat);
289
+ const tmpFilter = { [codeField]: newdocno}
290
+ const filter = this.applyIsolationFilter(appUser, tmpFilter,docType)
291
+ // console.log("filter",filter)
292
+ const existDoc = await collection.findOne(filter);
293
+ docFormat.nextNumber += 1
294
+ if (existDoc) {
295
+ continue;
296
+ }
297
+ accept=true;
298
+ // console.log('accepted at attempt=',attempt)
299
+ break;
300
+ }
301
+ // console.log('finally attempt',attempt+1)
302
+ return Promise.resolve({accept:accept,offset:attempt+1, newDocNo:newdocno})
303
+ }
233
304
  }
@@ -4,7 +4,8 @@
4
4
  * last change 2023-10-28
5
5
  * Author: Ks Tan
6
6
  */
7
- export const alldocuments:any[] = [
7
+ import { DocumentDictEntry } from 'src/simple-app/_core/framework/schemas';
8
+ export const alldocuments:DocumentDictEntry[] = [
8
9
  <%for(let i=0; i<it.modules.length;i++){ %>
9
10
  <% const d = it.modules[i] %>
10
11
  <% const conf = d.schema["x-simpleapp-config"] %>
@@ -0,0 +1,5 @@
1
+ import { UserContext } from '../../user-context/user.context';
2
+ import { CreatePayload } from '../event.type';
3
+ export interface EventErrorCreate<T> {
4
+ errorCreate(appUser: UserContext, payload: CreatePayload<T>,err:any): Promise<void>;
5
+ }
@@ -0,0 +1,5 @@
1
+ import { UserContext } from '../../user-context/user.context';
2
+ import { DeletePayload } from '../event.type';
3
+ export interface EventErrorDelete<T> {
4
+ errorDelete(appUser: UserContext, payload: DeletePayload<T>,err:any): Promise<void>;
5
+ }
@@ -0,0 +1,5 @@
1
+ import { UserContext } from '../../user-context/user.context';
2
+ import { PatchPayload } from '../event.type';
3
+ export interface EventErrorPatch<T> {
4
+ errorPatch(appUser: UserContext, payload: PatchPayload<T>,err:any): Promise<void>;
5
+ }
@@ -0,0 +1,5 @@
1
+ import { UserContext } from '../../user-context/user.context';
2
+ import { BeforeSetStatusPayload } from '../event.type';
3
+ export interface EventErrorSetStatus<T> {
4
+ errorSetStatus(appUser: UserContext, payload: BeforeSetStatusPayload<T>,err:any): Promise<void>;
5
+ }
@@ -0,0 +1,5 @@
1
+ import { UserContext } from '../../user-context/user.context';
2
+ import { UpdatePayload } from '../event.type';
3
+ export interface EventErrorUpdate<T> {
4
+ errorUpdate(appUser: UserContext, payload: UpdatePayload<T>,err:any): Promise<void>;
5
+ }
@@ -6,3 +6,7 @@ export * from './event-before-delete.interface';
6
6
  export * from './event-after-delete.interface';
7
7
  export * from './event-before-set-status.interface';
8
8
  export * from './event-after-set-status.interface';
9
+ export * from './event-error-create.interface';
10
+ export * from './event-error-update.interface';
11
+ export * from './event-error-delete.interface';
12
+ export * from './event-error-set-status.interface';
@@ -51,16 +51,13 @@ export class SimpleAppController<TService extends ServiceType, TApiSchema> {
51
51
  return this.service.fullTextSearch(appuser, body);
52
52
  }
53
53
 
54
- async _search(appuser: UserContext, searchObject: SearchBody, res?: Response) {
55
- const items = await this.service.searchWithPageInfo(
56
- appuser,
57
- searchObject['filter'],
58
- searchObject['fields'],
59
- searchObject['sorts'],
60
- searchObject['lookup'],
61
- searchObject['pagination'],
62
- );
63
-
54
+ async _search(appuser: UserContext, searchObject: SearchBody, res?: Response) {
55
+ //return this.service.search(appuser, searchObject['filter'], searchObject['fields'], searchObject['sorts'], searchObject['lookup']);
56
+ const items = await this.service.searchWithPageInfo(appuser, searchObject['filter'], searchObject['fields'], searchObject['sorts'], searchObject['lookup'], searchObject['pagination']);
57
+ const total = await this.service.getTotalCount(appuser, searchObject['filter'] || {});
58
+ const pageSize = searchObject['pagination']?.pageSize || items.length;
59
+ const pageNo = searchObject['pagination']?.pageNo || 0;
60
+ this.service.setPaginationHeaders(res, total, { pageSize, pageNo });
64
61
  return items;
65
62
  }
66
63
 
@@ -53,7 +53,8 @@ export class SimpleAppService<T extends SchemaFields> {
53
53
  protected documentIdentityLabel = 'label';
54
54
  protected documentName = '-unknowndocname-';
55
55
  protected documentType = '-unknowndoctype-';
56
- protected LIMITPERPAGE = 20;
56
+ protected LIMITPERPAGE = 500;
57
+ protected MAX_PAGE_SIZE = 100000;
57
58
  protected moreAutoCompleteField: MoreProjectionType = {};
58
59
  protected isolationtype: IsolationType = IsolationType.org;
59
60
  protected isolationFilter: any = {};
@@ -256,10 +257,9 @@ export class SimpleAppService<T extends SchemaFields> {
256
257
  projection: string[] = undefined,
257
258
  sort: any = undefined,
258
259
  lookup: { [key: string]: string } = undefined,
259
- pagination?: { pageSize?: number; pageNo?: number },
260
+ pagination?: { pageSize?: number; pageNo?: number },
260
261
  ) {
261
262
  try {
262
-
263
263
  const isolationFilter = { ...this.getIsolationFilter(appUser) };
264
264
  this.polishIsolationFilter(isolationFilter);
265
265
 
@@ -273,12 +273,12 @@ export class SimpleAppService<T extends SchemaFields> {
273
273
  if (lookup === undefined) {
274
274
  this.logger.debug('after search', newfilters);
275
275
  let query = this.doc.find(newfilters, projection, { session: appUser.getDBSession() }).sort(sort);
276
-
276
+
277
277
  if (pagination) {
278
278
  const { pageSize, pageNo, skip } = this.buildPagination(pagination);
279
279
  query = query.skip(skip).limit(pageSize);
280
280
  }
281
-
281
+
282
282
  searchResults = await query.exec();
283
283
  } else {
284
284
  const pipelines = this.searchToAggregate(appUser, newfilters, projection, sort, lookup, pagination);
@@ -321,7 +321,7 @@ export class SimpleAppService<T extends SchemaFields> {
321
321
  let pageSize: number;
322
322
  let pageNo: number;
323
323
  let skip: number;
324
-
324
+
325
325
  if (pagination !== undefined) {
326
326
  const paginationResult = this.buildPagination(pagination);
327
327
  pageSize = paginationResult.pageSize;
@@ -341,25 +341,13 @@ export class SimpleAppService<T extends SchemaFields> {
341
341
  const baseQuery = this.doc.find(newfilters, projection, { session: appUser.getDBSession() }).sort(sort);
342
342
  const query = baseQuery.skip(skip).limit(pageSize);
343
343
 
344
- const [rows, count] = await Promise.all([
345
- query.exec(),
346
- this.doc.countDocuments(newfilters).session(appUser.getDBSession()),
347
- ]);
344
+ const [rows, count] = await Promise.all([query.exec(), this.doc.countDocuments(newfilters).session(appUser.getDBSession())]);
348
345
  items = rows;
349
346
  total = count;
350
347
  } else {
351
- const pipelines = this.searchToAggregate(
352
- appUser,
353
- newfilters,
354
- projection as any,
355
- sort as any,
356
- lookup as any,
357
- { pageSize, pageNo },
358
- );
359
-
360
- const countPipeline: PipelineStage[] = [
361
- { $match: newfilters },
362
- ];
348
+ const pipelines = this.searchToAggregate(appUser, newfilters, projection as any, sort as any, lookup as any, { pageSize, pageNo });
349
+
350
+ const countPipeline: PipelineStage[] = [{ $match: newfilters }];
363
351
  const collections = Object.keys(lookup);
364
352
  collections.forEach((tokey: string) => {
365
353
  const toarr = tokey.split('.');
@@ -380,13 +368,10 @@ export class SimpleAppService<T extends SchemaFields> {
380
368
 
381
369
  this.logger.debug('after aggregate searchWithPageInfo', pipelines);
382
370
 
383
- const [rows, countResult] = await Promise.all([
384
- this.aggregate(appUser, pipelines),
385
- this.aggregate(appUser, countPipeline),
386
- ]);
387
-
371
+ const [rows, countResult] = await Promise.all([this.aggregate(appUser, pipelines), this.aggregate(appUser, countPipeline)]);
372
+
388
373
  items = rows as T[];
389
- total = countResult && countResult.length > 0 ? (countResult[0]['total'] || 0) : 0;
374
+ total = countResult && countResult.length > 0 ? countResult[0]['total'] || 0 : 0;
390
375
  }
391
376
 
392
377
  return items;
@@ -409,10 +394,10 @@ export class SimpleAppService<T extends SchemaFields> {
409
394
 
410
395
  setPaginationHeaders(res: Response, total?: number, pagination?: { pageSize?: number; pageNo?: number }) {
411
396
  if (!res || typeof res.setHeader !== 'function') return;
412
-
397
+
413
398
  if (total !== undefined && total !== null && pagination) {
414
399
  const { pageSize, pageNo } = this.buildPagination(pagination);
415
- const pageCount = pageSize > 0 && total > 0 ? Math.ceil(total / pageSize) : (total > 0 ? 1 : 0);
400
+ const pageCount = pageSize > 0 && total > 0 ? Math.ceil(total / pageSize) : total > 0 ? 1 : 0;
416
401
  res.setHeader('x-page-count', pageCount);
417
402
  res.setHeader('x-page-size', pageSize);
418
403
  res.setHeader('x-page-no', pageNo);
@@ -427,30 +412,28 @@ export class SimpleAppService<T extends SchemaFields> {
427
412
 
428
413
  buildPagination(pagination?: { pageSize?: number; pageNo?: number }) {
429
414
  let finalPageSize: number;
430
- if (pagination && typeof pagination.pageSize === 'number' && pagination.pageSize > 0) {
431
- finalPageSize = pagination.pageSize;
415
+ if (pagination && typeof pagination.pageSize === 'number') {
416
+ if (pagination.pageSize === 0) {
417
+ finalPageSize = this.MAX_PAGE_SIZE;
418
+ } else if (pagination.pageSize > 0) {
419
+ finalPageSize = Math.min(pagination.pageSize, this.MAX_PAGE_SIZE);
420
+ } else {
421
+ finalPageSize = this.MAX_PAGE_SIZE;
422
+ }
432
423
  } else if (pagination !== undefined) {
433
- finalPageSize = 10000;
424
+ finalPageSize = this.MAX_PAGE_SIZE;
434
425
  } else {
435
426
  finalPageSize = this.LIMITPERPAGE;
436
427
  }
437
-
428
+
438
429
  const rawPageNo = Number.isFinite(pagination?.pageNo) ? pagination.pageNo : 0;
439
430
  const pageNo = rawPageNo < 0 ? 0 : rawPageNo;
440
431
  const skip = pageNo * finalPageSize;
441
-
442
- this.logger.debug('buildPagination result:', {
443
- input: pagination,
444
- finalPageSize,
445
- pageNo,
446
- skip
447
- });
448
-
432
+
433
+
449
434
  return { pageSize: finalPageSize, skip, pageNo };
450
435
  }
451
436
 
452
-
453
-
454
437
  async fullTextSearch(appUser: UserContext, body: TextSearchBody) {
455
438
  try {
456
439
  //{filters:string[],fields:string[]}
@@ -544,9 +527,9 @@ export class SimpleAppService<T extends SchemaFields> {
544
527
 
545
528
  try {
546
529
  const result = await this.doc.insertMany(datas, { session: dbsession });
547
- const allIds = datas.map(item=>item._id)
548
- const allLogData = datas.map(item=>null)
549
- appUser.addTransactionStep('createMany',this.documentName,allIds,allLogData);
530
+ const allIds = datas.map((item) => item._id);
531
+ const allLogData = datas.map((item) => null);
532
+ appUser.addTransactionStep('createMany', this.documentName, allIds, allLogData);
550
533
 
551
534
  await this.addManyAuditEvents(appUser, this.documentName, 'createMany', datas);
552
535
  for (const data of datas) {
@@ -592,28 +575,21 @@ export class SimpleAppService<T extends SchemaFields> {
592
575
  this.applyNestedDateTime(appUser, data, 'create');
593
576
 
594
577
  //new way of hook
595
- await this.runEvent(appUser, this.setHookName('beforeCreate'), { data: data }, false);
578
+ await this.runEvent(appUser, 'beforeCreate', { data: data }, false);
596
579
 
597
580
  this.logger.debug(data, `Create Record ${this.documentName}`);
598
581
  const newdoc = new this.doc(data);
599
582
  await this.identifyForeignKeys(appUser, data);
600
583
  try {
601
584
  result = await newdoc.save({ session: dbsession });
602
- appUser.addTransactionStep('create',this.documentName,[data._id],[null]);
585
+ appUser.addTransactionStep('create', this.documentName, [data._id], [null]);
603
586
  await this.addAuditEvent(appUser, this.documentName, result._id, 'create', data);
604
587
  appUser.addInsertedRecordId(this.documentName, result._id);
605
- } catch (err) {
606
- this.logger.error(err);
607
- throw err;
608
- }
609
-
610
- try {
611
- //new way of hook
612
- await this.runEvent(appUser, this.setHookName('afterCreate'), { data: result }, false);
613
-
588
+ await this.runEvent(appUser, 'afterCreate', { data: result }, false);
614
589
  await this.callWebhook(appUser, 'create', result);
615
590
  return result as T;
616
591
  } catch (err) {
592
+ await this.runErrorEvent(appUser, 'errorCreate', data, err)
617
593
  throw new InternalServerErrorException(`createHook ${this.documentType} error: ${JSON.stringify(err)}`, `${this.documentType} createHook error`);
618
594
  }
619
595
  }
@@ -725,7 +701,7 @@ export class SimpleAppService<T extends SchemaFields> {
725
701
  let dependency;
726
702
  try {
727
703
  //new way of hook
728
- await this.runEvent(appUser, this.setHookName('beforeDelete'), { id: id, deleteData: deletedata }, false);
704
+ await this.runEvent(appUser, 'beforeDelete', { id: id, deleteData: deletedata }, false);
729
705
 
730
706
  // if (this.hooks.beforeDelete) await this.hooks.beforeDelete(appUser, id, deletedata);
731
707
  this.logger.debug('delete record', this.documentName, id);
@@ -737,8 +713,7 @@ export class SimpleAppService<T extends SchemaFields> {
737
713
  filterIsolation['_id'] = id;
738
714
  this.logger.debug('delete filter', filterIsolation);
739
715
  const result = await this.doc.deleteOne(filterIsolation).session(dbsession);
740
- appUser.addTransactionStep('delete',this.documentName,[id],[deletedata]);
741
-
716
+ appUser.addTransactionStep('delete', this.documentName, [id], [deletedata]);
742
717
 
743
718
  await this.addAuditEvent(appUser, this.documentName, id, 'delete', deletedata);
744
719
 
@@ -751,7 +726,7 @@ export class SimpleAppService<T extends SchemaFields> {
751
726
  // this.doc.findByIdAndDelete(id)
752
727
 
753
728
  //new way of hook
754
- await this.runEvent(appUser, this.setHookName('afterDelete'), { id: id, deleteData: deletedata }, false);
729
+ await this.runEvent(appUser, 'afterDelete', { id: id, deleteData: deletedata }, false);
755
730
 
756
731
  await this.callWebhook(appUser, 'delete', deletedata);
757
732
  return deleteresult;
@@ -764,6 +739,7 @@ export class SimpleAppService<T extends SchemaFields> {
764
739
  if (err instanceof ForbiddenException) {
765
740
  throw new ForbiddenException(err);
766
741
  } else {
742
+ await this.runErrorEvent(appUser, 'errorDelete', deletedata, err)
767
743
  throw new InternalServerErrorException(err);
768
744
  } //JSON.stringify(dependency),JSON.stringify(dependency)
769
745
  }
@@ -848,7 +824,7 @@ export class SimpleAppService<T extends SchemaFields> {
848
824
  await this.identifyForeignKeys(appUser, data);
849
825
 
850
826
  //new way of hook
851
- await this.runEvent(appUser, this.setHookName('beforeUpdate'), { id: id, prevData: existingdata, newData: data }, false);
827
+ await this.runEvent(appUser, 'beforeUpdate', { id: id, prevData: existingdata, newData: data }, false);
852
828
 
853
829
  // if (this.hooks.beforeUpdate) await this.hooks.beforeUpdate(appUser, id, existingdata, data);
854
830
 
@@ -876,16 +852,17 @@ export class SimpleAppService<T extends SchemaFields> {
876
852
  new: true,
877
853
  });
878
854
 
879
- appUser.addTransactionStep('update',this.documentName,[id],[existingdata]);
855
+ appUser.addTransactionStep('update', this.documentName, [id], [existingdata]);
880
856
  await this.addAuditEvent(appUser, this.documentName, id, 'update', data);
881
857
 
882
858
  appUser.addUpdatedRecordId(this.documentName, data._id);
883
- await this.runEvent(appUser, this.setHookName('afterUpdate'), { id: id, prevData: existingdata, newData: data }, false);
859
+ await this.runEvent(appUser, 'afterUpdate', { id: id, prevData: existingdata, newData: data }, false);
884
860
 
885
861
  await this.callWebhook(appUser, 'update', result);
886
862
  return result; // await this.findById(appUser, id);
887
863
  } catch (err) {
888
864
  this.logger.error(err);
865
+ await this.runErrorEvent(appUser, 'errorUpdate', data, err)
889
866
  throw new InternalServerErrorException(err.message);
890
867
  }
891
868
  };
@@ -909,7 +886,7 @@ export class SimpleAppService<T extends SchemaFields> {
909
886
 
910
887
  await this.identifyForeignKeys(appUser, data);
911
888
 
912
- await this.runEvent(appUser, this.setHookName('beforeUpdate'), { data: data }, false);
889
+ await this.runEvent(appUser, 'beforeUpdate', { data: data }, false);
913
890
 
914
891
  // if (this.hooks.beforeUpdate) await this.hooks.beforeUpdate(appUser, id, existingdata, data);
915
892
 
@@ -937,10 +914,10 @@ export class SimpleAppService<T extends SchemaFields> {
937
914
  new: true,
938
915
  });
939
916
 
940
- appUser.addTransactionStep('update',this.documentName,[id],[existingdata]);
917
+ appUser.addTransactionStep('update', this.documentName, [id], [existingdata]);
941
918
  await this.addAuditEvent(appUser, this.documentName, id, 'update', data);
942
919
 
943
- await this.runEvent(appUser, this.setHookName('afterUpdate'), { id: id, prevData: existingdata, newData: result }, false);
920
+ await this.runEvent(appUser, 'afterUpdate', { id: id, prevData: existingdata, newData: result }, false);
944
921
  appUser.addUpdatedRecordId(this.documentName, data._id);
945
922
 
946
923
  await this.callWebhook(appUser, 'update', result);
@@ -970,7 +947,7 @@ export class SimpleAppService<T extends SchemaFields> {
970
947
 
971
948
  //patch not suitable trigger afterupdate
972
949
  // if (this.hooks.beforeUpdate) await this.hooks.beforeUpdate(appUser, id, existingdata, data);
973
- await this.runEvent(appUser, this.setHookName('beforePatch'), { id: id, patchData: data, prevData: existingdata }, false);
950
+ await this.runEvent(appUser, 'beforePatch', { id: id, patchData: data, prevData: existingdata }, false);
974
951
  const dbsession = appUser.getDBSession();
975
952
  if (!appUser.inTransaction()) {
976
953
  appUser.startTransaction();
@@ -998,13 +975,13 @@ export class SimpleAppService<T extends SchemaFields> {
998
975
  session: dbsession,
999
976
  new: true,
1000
977
  });
1001
- appUser.addTransactionStep('update',this.documentName,[id],[existingdata]);
978
+ appUser.addTransactionStep('update', this.documentName, [id], [existingdata]);
1002
979
  //skip audit trail, useful when want to patch x-foreignkey code,label
1003
980
  if (!skipLog) {
1004
981
  await this.addAuditEvent(appUser, this.documentName, id, 'patch', data);
1005
982
  }
1006
983
  appUser.addUpdatedRecordId(this.documentName, data._id);
1007
- await this.runEvent(appUser, this.setHookName('afterPatch'), { id: id, patchData: data, prevData: existingdata }, false);
984
+ await this.runEvent(appUser, 'afterPatch', { id: id, patchData: data, prevData: existingdata }, false);
1008
985
  return result; //await this.findById(appUser, id);
1009
986
  } catch (err) {
1010
987
  this.logger.error(err.message, 'findIdThenPath error');
@@ -1023,10 +1000,9 @@ export class SimpleAppService<T extends SchemaFields> {
1023
1000
  // const result = await this.doc.insertMany(datas, { session: dbsession });
1024
1001
  // const allIds = datas.map(item=>item._id)
1025
1002
  // const allLogData = datas.map(item=>null)
1026
-
1027
1003
 
1028
1004
  const result = await this.doc.deleteMany({ _id: { $in: ids } }, { session: dbsession });
1029
- appUser.addTransactionStep('deleteMany',this.documentName,ids,searchResult);
1005
+ appUser.addTransactionStep('deleteMany', this.documentName, ids, searchResult);
1030
1006
  // console.log(`Delete ${this.documentName} ids`, ids, result);
1031
1007
  for (const id of ids) {
1032
1008
  appUser.addDeletedRecordId(this.documentName, id);
@@ -1057,7 +1033,7 @@ export class SimpleAppService<T extends SchemaFields> {
1057
1033
  const ids = searchResult.map((row) => row._id);
1058
1034
 
1059
1035
  const result = await this.doc.updateMany(isolationFilter, { $set: patch }, { session: dbsession });
1060
- appUser.addTransactionStep('updateMany',this.documentName,ids,searchResult);
1036
+ appUser.addTransactionStep('updateMany', this.documentName, ids, searchResult);
1061
1037
  await this.addManyAuditEvents(appUser, this.documentName, 'patchMany', [patch]);
1062
1038
 
1063
1039
  return result;
@@ -1136,25 +1112,28 @@ export class SimpleAppService<T extends SchemaFields> {
1136
1112
  }
1137
1113
  data['documentStatus'] = docstatus;
1138
1114
  // await this.hook(appUser, HookType.beforeSetStatus, data);
1139
- await this.runEvent(appUser, this.setHookName('beforeSetStatus'), { docStatus: docstatus, prevData: existdata, newData: data }, false);
1115
+ await this.runEvent(appUser, 'beforeSetStatus', { docStatus: docstatus, prevData: existdata, newData: data }, false);
1140
1116
 
1141
1117
  // if (this.hooks.beforeSetStatus) await this.hooks.beforeSetStatus(appUser, docstatus, data, existdata);
1118
+ try{
1119
+ if (data && !data['created']) {
1120
+ const createresult = await this.create(appUser, data);
1121
+ await this.runEvent(appUser, 'afterSetStatus', { docStatus: docstatus, data: createresult }, false);
1122
+ // if (this.hooks.afterSetStatus) await this.hooks.afterSetStatus(appUser, docstatus, createresult);
1123
+ await this.addAuditEvent(appUser, this.documentName, id, docstatus, data);
1124
+
1125
+ return createresult;
1126
+ } else {
1127
+ const updateresult = await this.findIdThenPatch(appUser, id, data);
1128
+ const finaldata = await this.findById(appUser, id);
1129
+ await this.runEvent(appUser, 'afterSetStatus', { docStatus: docstatus, data: finaldata }, false);
1130
+ await this.addAuditEvent(appUser, this.documentName, id, docstatus, data);
1142
1131
 
1143
- if (data && !data['created']) {
1144
- const createresult = await this.create(appUser, data);
1145
- await this.runEvent(appUser, this.setHookName('afterSetStatus'), { docStatus: docstatus, data: createresult }, false);
1146
- // if (this.hooks.afterSetStatus) await this.hooks.afterSetStatus(appUser, docstatus, createresult);
1147
- await this.addAuditEvent(appUser, this.documentName, id, docstatus, data);
1148
-
1149
- return createresult;
1150
- } else {
1151
- const updateresult = await this.findIdThenPatch(appUser, id, data);
1152
- const finaldata = await this.findById(appUser, id);
1153
- await this.runEvent(appUser, this.setHookName('afterSetStatus'), { docStatus: docstatus, data: finaldata }, false);
1154
- await this.addAuditEvent(appUser, this.documentName, id, docstatus, data);
1155
-
1156
- await this.callWebhook(appUser, docstatus, finaldata);
1157
- return updateresult;
1132
+ await this.callWebhook(appUser, docstatus, finaldata);
1133
+ return updateresult;
1134
+ }
1135
+ }catch(e){
1136
+ await this.runErrorEvent(appUser, 'errorSetStatus', data, e)
1158
1137
  }
1159
1138
  }
1160
1139
 
@@ -1187,7 +1166,8 @@ export class SimpleAppService<T extends SchemaFields> {
1187
1166
  * @param {any} data The data
1188
1167
  * @return {Promise} { description_of_the_return_value }
1189
1168
  */
1190
- async runEvent(appUser: UserContext, eventName: string, payloads: any, enforce: boolean = true) {
1169
+ async runEvent(appUser: UserContext, shortEventName: string, payloads: any, enforce: boolean = true) {
1170
+ const eventName = this.setHookName(shortEventName)
1191
1171
  try {
1192
1172
  if (enforce && !this.eventEmitter.hasListeners(eventName)) {
1193
1173
  throw new InternalServerErrorException(`${eventName} seems no listener`);
@@ -1207,6 +1187,19 @@ export class SimpleAppService<T extends SchemaFields> {
1207
1187
  console.error(e);
1208
1188
  throw e;
1209
1189
  }
1190
+ }
1191
+
1192
+ async runErrorEvent(appUser: UserContext, shortEventName: string, payloads: any, error:any) {
1193
+ const eventName = this.setHookName(shortEventName)
1194
+ try{
1195
+ if (this.eventEmitter.hasListeners(eventName)) {
1196
+ console.log('run eventName', eventName);
1197
+ const res = await this.eventEmitter.emitAsync(eventName, appUser, payloads,error);
1198
+ }
1199
+ }catch(e){
1200
+ //runErrorEvent wont handle more error
1201
+ }
1202
+
1210
1203
  }
1211
1204
  // startWorkflow(appUser: UserContext, processName: WorkflowName, workflowData: any) {
1212
1205
  // return this.eventEmitter.emit('workflow.start', appUser, processName, workflowData);
@@ -1332,14 +1325,7 @@ export class SimpleAppService<T extends SchemaFields> {
1332
1325
  return response;
1333
1326
  }
1334
1327
 
1335
- searchToAggregate(
1336
- appUser: UserContext,
1337
- filter: FilterQuery<T>,
1338
- columns: string[],
1339
- sort: string[][],
1340
- lookup: { [key: string]: string },
1341
- pagination?: { pageSize?: number; pageNo?: number },
1342
- ) {
1328
+ searchToAggregate(appUser: UserContext, filter: FilterQuery<T>, columns: string[], sort: string[][], lookup: { [key: string]: string }, pagination?: { pageSize?: number; pageNo?: number }) {
1343
1329
  const pipelines: PipelineStage[] = [];
1344
1330
  const projection = {};
1345
1331
  // console.log('sortsort', sort);
@@ -1382,13 +1368,13 @@ export class SimpleAppService<T extends SchemaFields> {
1382
1368
  });
1383
1369
  pipelines.push({ $sort: sortobj });
1384
1370
  }
1385
-
1371
+
1386
1372
  if (pagination) {
1387
1373
  const { pageSize, skip } = this.buildPagination(pagination);
1388
1374
  pipelines.push({ $skip: skip });
1389
1375
  pipelines.push({ $limit: pageSize });
1390
1376
  }
1391
-
1377
+
1392
1378
  //this.logger.warn( pipelines,'pipelinespipelinespipelines',);
1393
1379
 
1394
1380
  return pipelines;
@@ -176,4 +176,16 @@ export class BranchPermission extends Permission {
176
176
  branch: ForeignKey;
177
177
  }
178
178
 
179
- export type StepData = {action:string,collection:string,id:string[],data:any[]}
179
+ export type StepData = {action:string,collection:string,id:string[],data:any[]}
180
+
181
+ export type DocumentDictEntry = {
182
+ docName: string;
183
+ docType: string;
184
+ page: string;
185
+ isolationType: string;
186
+ documentDate: string;
187
+ docNumber: boolean;
188
+ docNoPattern: string;
189
+ webhook?: string[];
190
+ uniqueKey?: string;
191
+ };