@simitgroup/simpleapp-generator 1.6.6-j-alpha → 1.6.6-k.1-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.
Files changed (158) hide show
  1. package/ReleaseNote.md +3 -2
  2. package/dist/buildinschemas/docnoformat.js +6 -6
  3. package/dist/buildinschemas/docnoformat.js.map +1 -1
  4. package/dist/buildinschemas/documentevent.js +1 -1
  5. package/dist/buildinschemas/documentevent.js.map +1 -1
  6. package/dist/buildinschemas/organization.js +1 -1
  7. package/dist/buildinschemas/organization.js.map +1 -1
  8. package/dist/buildinschemas/tenant.js +1 -1
  9. package/dist/buildinschemas/tenant.js.map +1 -1
  10. package/dist/buildinschemas/user.js +2 -2
  11. package/dist/buildinschemas/user.js.map +1 -1
  12. package/dist/framework.d.ts.map +1 -1
  13. package/dist/framework.js +2 -0
  14. package/dist/framework.js.map +1 -1
  15. package/dist/generate.d.ts.map +1 -1
  16. package/dist/generate.js +21 -20
  17. package/dist/generate.js.map +1 -1
  18. package/dist/processors/jrxmlbuilder.js +3 -3
  19. package/dist/processors/jrxmlbuilder.js.map +1 -1
  20. package/dist/processors/jsonschemabuilder.d.ts.map +1 -1
  21. package/dist/processors/jsonschemabuilder.js +17 -10
  22. package/dist/processors/jsonschemabuilder.js.map +1 -1
  23. package/dist/type.d.ts +1 -1
  24. package/dist/type.d.ts.map +1 -1
  25. package/package.json +1 -2
  26. package/reset-install.sh +3 -0
  27. package/src/buildinschemas/docnoformat.ts +6 -6
  28. package/src/buildinschemas/documentevent.ts +1 -1
  29. package/src/buildinschemas/organization.ts +1 -1
  30. package/src/buildinschemas/tenant.ts +1 -1
  31. package/src/buildinschemas/user.ts +2 -2
  32. package/src/framework.ts +2 -1
  33. package/src/generate.ts +22 -20
  34. package/src/processors/jrxmlbuilder.ts +3 -3
  35. package/src/processors/jsonschemabuilder.ts +27 -14
  36. package/src/type.ts +1 -1
  37. package/templates/basic/nuxt/simpleapp.generate.client.ts.eta +3 -3
  38. package/templates/nest/.gitignore.eta +4 -5
  39. package/templates/nest/src/{app.module.ts.eta → app.module.ts._eta} +2 -2
  40. package/templates/nest/src/{main.ts.eta → main.ts._eta} +2 -2
  41. package/templates/nest/src/printapi/printapi.service.ts.eta +7 -8
  42. package/templates/nest/src/simpleapp/.gitignore.eta +1 -1
  43. package/templates/nest/src/simpleapp/additional.module.ts._eta +13 -0
  44. package/templates/nest/src/simpleapp/generate/commons/audittrail.service.ts.eta +2 -2
  45. package/templates/nest/src/simpleapp/generate/commons/customkeycloa.guard.ts.eta +14 -12
  46. package/templates/nest/src/simpleapp/generate/commons/customkeycloak.guard.ts.eta +36 -42
  47. package/templates/nest/src/simpleapp/generate/commons/docnogenerator.service.ts.eta +6 -3
  48. package/templates/nest/src/simpleapp/generate/commons/encryption.static.ts.eta +56 -61
  49. package/templates/nest/src/simpleapp/generate/commons/middlewares/tenant.middleware.ts.eta +65 -88
  50. package/templates/nest/src/simpleapp/generate/commons/robotuser.service.ts.eta +10 -9
  51. package/templates/nest/src/simpleapp/generate/commons/roles/roles.guard.ts.eta +10 -3
  52. package/templates/nest/src/simpleapp/generate/commons/runwebhook.service.ts.eta +41 -39
  53. package/templates/nest/src/simpleapp/generate/commons/user.context.ts.eta +439 -293
  54. package/templates/nest/src/simpleapp/generate/processors/autoinc.processor.ts.eta +20 -5
  55. package/templates/nest/src/simpleapp/generate/processors/simpleapp.processor.ts.eta +437 -40
  56. package/templates/nest/src/simpleapp/profile/{profile.controller.ts.eta → profile.controller.ts._eta} +2 -2
  57. package/templates/nest/src/simpleapp/profile/{profile.service.ts.eta → profile.service.ts._eta} +2 -2
  58. package/templates/nest/src/simpleapp/simpleapp.module.ts.eta +19 -15
  59. package/templates/nuxt/components/button/ButtonMultiple.vue._eta +1 -1
  60. package/templates/nuxt/components/calendar/{CalendarByResource.vue.eta → CalendarByResource.vue._eta} +2 -2
  61. package/templates/nuxt/components/calendar/{CalendarInput.vue.eta → CalendarInput.vue._eta} +2 -2
  62. package/templates/nuxt/components/calendar/{CalendarSmall.vue.eta → CalendarSmall.vue._eta} +6 -0
  63. package/templates/nuxt/components/debug/{DebugDocumentData.vue.eta → DebugDocumentData.vue._eta} +2 -2
  64. package/templates/nuxt/components/form/{FormDocnoformat.vue.eta → FormDocnoformat.vue._eta} +2 -1
  65. package/templates/nuxt/components/form/user/{FormUserPermission.vue.eta → FormUserPermission.vue._eta} +6 -0
  66. package/templates/nuxt/components/header/{HeaderBreadcrumb.vue.eta → HeaderBreadcrumb.vue._eta} +2 -2
  67. package/templates/nuxt/components/header/button/{HeaderButtonProfile.vue.eta → HeaderButtonProfile.vue._eta} +2 -2
  68. package/templates/nuxt/components/header/button/task/{HeaderButtonTaskList.vue.eta → HeaderButtonTaskList.vue._eta} +2 -2
  69. package/templates/nuxt/components/image/ImageAvatar.vue.eta +29 -30
  70. package/templates/nuxt/components/image/ImageOrganization.vue.eta +15 -9
  71. package/templates/nuxt/components/image/ImageToBase64Uploader.vue.eta +28 -18
  72. package/templates/nuxt/components/list/{ListDocumentTable.vue.eta → ListDocumentTable.vue._eta} +2 -2
  73. package/templates/nuxt/components/list/{ListItem.vue.eta → ListItem.vue._eta} +2 -2
  74. package/templates/nuxt/components/list/{ListMessages.vue.eta → ListMessages.vue._eta} +6 -0
  75. package/templates/nuxt/components/list/{ListView.vue.eta → ListView.vue._eta} +2 -2
  76. package/templates/nuxt/components/overlay/{OverlayPanelWithToolBar.vue.eta → OverlayPanelWithToolBar.vue._eta} +2 -2
  77. package/templates/nuxt/components/overlay/{OverlaySideBarCrud.vue.eta → OverlaySideBarCrud.vue._eta} +2 -2
  78. package/templates/nuxt/components/overlay/{OverlayViewer.vue.eta → OverlayViewer.vue._eta} +2 -2
  79. package/templates/nuxt/components/page/{PageDocList.vue.eta → PageDocList.vue._eta} +2 -2
  80. package/templates/nuxt/components/renderer/{RendererDateTime.vue.eta → RendererDateTime.vue._eta} +2 -2
  81. package/templates/nuxt/components/renderer/{RendererDocHistories.vue.eta → RendererDocHistories.vue._eta} +6 -0
  82. package/templates/nuxt/components/renderer/{RendererForeignKey.vue.eta → RendererForeignKey.vue._eta} +2 -2
  83. package/templates/nuxt/components/renderer/{RendererLink.vue.eta → RendererLink.vue._eta} +2 -2
  84. package/templates/nuxt/components/renderer/{RendererTime.vue.eta → RendererTime.vue._eta} +2 -2
  85. package/templates/nuxt/components/renderer/{RendererViewer.vue.eta → RendererViewer.vue._eta} +2 -2
  86. package/templates/nuxt/components/select/{SelectTemplate.vue.eta → SelectTemplate.vue._eta} +2 -2
  87. package/templates/nuxt/components/session/{SessionBlock.vue.eta → SessionBlock.vue._eta} +2 -2
  88. package/templates/nuxt/components/simpleApp/SimpleAppAutocomplete.vue.eta +55 -55
  89. package/templates/nuxt/components/simpleApp/SimpleAppCalendarInput.vue.eta +13 -11
  90. package/templates/nuxt/components/simpleApp/SimpleAppChildrenList.vue.eta +6 -3
  91. package/templates/nuxt/components/simpleApp/SimpleAppDocumentNo.vue.eta +32 -27
  92. package/templates/nuxt/components/simpleApp/SimpleAppFieldContainer.vue.eta +19 -28
  93. package/templates/nuxt/components/simpleApp/SimpleAppInput.vue.eta +114 -105
  94. package/templates/nuxt/components/simpleApp/SimpleAppInputTable.vue.eta +3 -3
  95. package/templates/nuxt/components/simpleApp/SimpleAppJsonSchemaForm.vue.eta +1 -1
  96. package/templates/nuxt/components/simpleApp/SimpleAppUserPicker.vue.eta +10 -12
  97. package/templates/nuxt/components/table/{TableDocuments.vue.eta → TableDocuments.vue._eta} +2 -2
  98. package/templates/nuxt/components/user/{UserButtonPermissionInfo.vue.eta → UserButtonPermissionInfo.vue._eta} +2 -2
  99. package/templates/nuxt/components/user/{UserInvitation.vue.eta → UserInvitation.vue._eta} +2 -2
  100. package/templates/nuxt/components/user/{UserProfileListItem.vue.eta → UserProfileListItem.vue._eta} +2 -2
  101. package/templates/nuxt/components/user/{UserTenantPicker.vue.eta → UserTenantPicker.vue._eta} +2 -2
  102. package/templates/nuxt/composables/getOpenApi.generate.ts.eta +11 -3
  103. package/templates/nuxt/composables/getUserStore.generate.ts.eta +1 -1
  104. package/templates/nuxt/composables/graphquery.generate.ts.eta +1 -1
  105. package/templates/nuxt/composables/recently.generate.ts.eta +0 -1
  106. package/templates/nuxt/composables/stringHelper.generate.ts.eta +5 -2
  107. package/templates/nuxt/pages/[xorg]/docnoformat/[id].vue.eta +0 -1
  108. package/templates/nuxt/pages/[xorg]/{docnoformat.vue.eta → docnoformat.vue._eta} +1 -0
  109. package/templates/nuxt/pages/[xorg]/organization/[id].vue.eta +13 -9
  110. package/templates/nuxt/pages/[xorg]/organization/new.vue.eta +10 -12
  111. package/templates/nuxt/pages/[xorg]/{user.vue.eta → user.vue._eta} +2 -2
  112. package/templates/nuxt/pages/{profile.vue.eta → profile.vue._eta} +2 -2
  113. package/templates/nuxt/plugins/10.simpleapp-event.ts.eta +6 -0
  114. package/templates/nuxt/plugins/20.simpleapp-userstore.ts.eta +89 -12
  115. package/templates/nuxt/plugins/40.pusher.ts.eta +6 -9
  116. package/templates/nuxt/providers/my-provider.ts.eta +1 -1
  117. package/templates/nuxt/server/api/profile/[...].ts.eta +4 -3
  118. package/templates/nuxt/simpleapp/generate/clients/SimpleAppClient.ts.eta +8 -4
  119. package/templates/nuxt/types/schema.ts.eta +1 -1
  120. package/templates/nuxt/types/user.ts.eta +6 -1
  121. package/templates/project/jsonschemas/appintegration.json_eta +227 -0
  122. package/templates/project/sharelibs/validate.ts.eta +1 -1
  123. package/dist/buildinschemas/message.d.ts +0 -3
  124. package/dist/buildinschemas/message.d.ts.map +0 -1
  125. package/dist/buildinschemas/message.js +0 -34
  126. package/dist/buildinschemas/message.js.map +0 -1
  127. package/dist/buildinschemas/webhookhistory.d.ts +0 -3
  128. package/dist/buildinschemas/webhookhistory.d.ts.map +0 -1
  129. package/dist/buildinschemas/webhookhistory.js +0 -44
  130. package/dist/buildinschemas/webhookhistory.js.map +0 -1
  131. package/dist/createproject.js +0 -138
  132. package/dist/createproject.js.map +0 -1
  133. package/dist/generate-allow-changebackend.js +0 -305
  134. package/dist/generate-allow-changebackend.js.map +0 -1
  135. package/dist/index2.js +0 -118
  136. package/dist/index2.js.map +0 -1
  137. package/dist/installdependency.js +0 -20
  138. package/dist/installdependency.js.map +0 -1
  139. package/dist/installnest.js +0 -2
  140. package/dist/installnest.js.map +0 -1
  141. package/dist/installnuxt.js +0 -2
  142. package/dist/installnuxt.js.map +0 -1
  143. package/dist/processors/groupsbuilder.js +0 -2
  144. package/dist/processors/groupsbuilder.js.map +0 -1
  145. package/dist/schematype/baseschema.js +0 -25
  146. package/dist/schematype/baseschema.js.map +0 -1
  147. package/dist/schematype/default.js +0 -2
  148. package/dist/schematype/default.js.map +0 -1
  149. package/dist/schematype/index.js +0 -12
  150. package/dist/schematype/index.js.map +0 -1
  151. package/dist/schematype/primarymasterdata.js +0 -38
  152. package/dist/schematype/primarymasterdata.js.map +0 -1
  153. package/dist/schematype/simple.js +0 -24
  154. package/dist/schematype/simple.js.map +0 -1
  155. package/dist/schematype/simplemasterdata.js +0 -31
  156. package/dist/schematype/simplemasterdata.js.map +0 -1
  157. package/dist/schematype/transaction.js +0 -74
  158. package/dist/schematype/transaction.js.map +0 -1
@@ -20,7 +20,9 @@ import {
20
20
  FilterQuery,
21
21
  ProjectionType,
22
22
  } from 'mongoose';
23
- import { getValidateService } from 'src/simpleapp/generate/sharelibs/validate';
23
+ import Ajv from 'ajv';
24
+ import addFormats from 'ajv-formats';
25
+ import addErrors from 'ajv-errors';
24
26
  import {
25
27
  NotFoundException,
26
28
  HttpException,
@@ -40,7 +42,10 @@ import {
40
42
  DeleteResultType,
41
43
  WorkflowName,
42
44
  TextSearchBody,
45
+ CheckMutipleUnionExistResponse,
43
46
  } from '../types';
47
+ import { CustomException } from 'src/customexception';
48
+
44
49
  @Injectable()
45
50
  export class SimpleAppService<T extends { _id?: string; __v?: number }> {
46
51
  @Inject(EventEmitter2)
@@ -112,6 +117,8 @@ export class SimpleAppService<T extends { _id?: string; __v?: number }> {
112
117
  this.data = { ...newdata };
113
118
  return this;
114
119
  };
120
+ getDocumentIdentityCode = () => this.documentIdentityCode;
121
+ getDocumentIdentityLabel = () => this.documentIdentityLabel;
115
122
  public isReadOnly(): boolean {
116
123
  return false;
117
124
  }
@@ -162,7 +169,7 @@ export class SimpleAppService<T extends { _id?: string; __v?: number }> {
162
169
  try {
163
170
  const filter1 = {};
164
171
  const filter2 = {};
165
- let filters: any[] = [];
172
+ const filters: any[] = [];
166
173
  if (
167
174
  this.jsonschema.properties[this.documentIdentityCode]['type'] ==
168
175
  'string'
@@ -176,7 +183,7 @@ export class SimpleAppService<T extends { _id?: string; __v?: number }> {
176
183
  const filterobj = { $or: filters };
177
184
  Object.assign(filterobj, data);
178
185
  Object.assign(filterobj, this.getIsolationFilter(appuser));
179
- let projections = {
186
+ const projections = {
180
187
  label: `\$${this.documentIdentityLabel}`,
181
188
  code: `\$${this.documentIdentityCode}`,
182
189
  };
@@ -184,7 +191,7 @@ export class SimpleAppService<T extends { _id?: string; __v?: number }> {
184
191
  Object.assign(projections, this.moreAutoCompleteField);
185
192
  }
186
193
  const products = await this.doc.find(filterobj, projections, {
187
- limit: this.LIMITPERPAGE,
194
+ // limit: this.LIMITPERPAGE,
188
195
  });
189
196
  const productlist = products.map((p: T) => {
190
197
  return p;
@@ -201,10 +208,7 @@ export class SimpleAppService<T extends { _id?: string; __v?: number }> {
201
208
  * @param filters
202
209
  * @returns
203
210
  */
204
- private async searchNoIsolation(
205
- appuser: UserContext,
206
- filters: FilterQuery<T>,
207
- ) {
211
+ async searchNoIsolation(appuser: UserContext, filters: FilterQuery<T>) {
208
212
  try {
209
213
  if (this.hooks.beforeSearch)
210
214
  await this.hooks.beforeSearch(appuser, filters);
@@ -222,6 +226,23 @@ export class SimpleAppService<T extends { _id?: string; __v?: number }> {
222
226
  }
223
227
  // return this;
224
228
  }
229
+ async aggregateNoIsolation(appuser: UserContext, pipeline: PipelineStage[]) {
230
+ if (pipeline[0] && pipeline[0]['$match']) {
231
+ try {
232
+ this.logger.verbose(pipeline, `${this.documentName} aggregate:`);
233
+
234
+ return await this.doc.aggregate(pipeline, {
235
+ session: appuser.getDBSession(),
236
+ });
237
+ } catch (err) {
238
+ throw new InternalServerErrorException(err);
239
+ }
240
+ } else {
241
+ throw new InternalServerErrorException(
242
+ 'first aggregate pipelinestage shall use $match',
243
+ );
244
+ }
245
+ }
225
246
  async aggregate(appuser: UserContext, pipeline: PipelineStage[]) {
226
247
  if (pipeline[0] && pipeline[0]['$match']) {
227
248
  try {
@@ -269,13 +290,12 @@ export class SimpleAppService<T extends { _id?: string; __v?: number }> {
269
290
  } else {
270
291
  const pipelines = this.searchToAggregate(
271
292
  appuser,
272
- filters,
293
+ newfilters,
273
294
  projection,
274
295
  sort,
275
296
  lookup,
276
297
  );
277
298
  this.logger.debug('after aggregate', pipelines);
278
-
279
299
  searchResults = await this.aggregate(appuser, pipelines);
280
300
  }
281
301
 
@@ -294,16 +314,60 @@ export class SimpleAppService<T extends { _id?: string; __v?: number }> {
294
314
  }
295
315
 
296
316
  async fullTextSearch(appuser: UserContext, body: TextSearchBody) {
297
- const isolationFilter = { ...this.getIsolationFilter(appuser) };
298
- this.polishIsolationFilter(isolationFilter);
317
+ try {
318
+ const isolationFilter = { ...this.getIsolationFilter(appuser) };
319
+ this.polishIsolationFilter(isolationFilter);
320
+ const keyword = body.keyword;
299
321
 
300
- const filters = { $text: { $search: body.keyword } };
301
- const newfilters: FilterQuery<T> = { ...filters, ...isolationFilter };
302
- const fields = body.fields;
303
- const sorts = body.sorts;
304
- //not yet support lookup mapper
305
- const lookup = body.lookup;
306
- return await this.doc.find(newfilters, fields, sorts);
322
+ // Prepare regex filter
323
+ const orConditions: any[] = [];
324
+ if (body.fields && body.fields.length > 0) {
325
+ body.fields.forEach((field: string) => {
326
+ if (
327
+ field != 'defaultPrice' &&
328
+ field != 'amount' &&
329
+ field != 'active' &&
330
+ field != 'updated'
331
+ ) {
332
+ orConditions.push({
333
+ [field]: { $regex: keyword, $options: 'i' },
334
+ });
335
+ } else {
336
+ if (
337
+ !isNaN(Number(keyword)) &&
338
+ (field == 'defaultPrice' || field == 'amount')
339
+ ) {
340
+ orConditions.push({
341
+ [field]: { $gte: keyword },
342
+ });
343
+ }
344
+ }
345
+ });
346
+ }
347
+ const filter = {
348
+ $or: orConditions,
349
+ };
350
+
351
+ const tmpfilters: FilterQuery<T> = { ...filter, ...isolationFilter };
352
+ const newfilters: FilterQuery<T> = {
353
+ $and: [tmpfilters],
354
+ };
355
+
356
+ const fields = body.fields || {};
357
+ const sorts = body.sorts || {};
358
+
359
+ // Ensure `this.doc` is a Mongoose model or similar
360
+ const results = await this.doc
361
+ .find(newfilters, fields)
362
+ .sort(sorts)
363
+ .exec();
364
+
365
+ return results;
366
+ } catch (error) {
367
+ console.error('Error during full-text search:', error);
368
+ // Ensure error handling returns a valid status code
369
+ throw new Error('Failed to perform search');
370
+ }
307
371
  }
308
372
  async findById(appuser: UserContext, id: string) {
309
373
  if (this.hooks.beforeFetchRecord)
@@ -321,6 +385,129 @@ export class SimpleAppService<T extends { _id?: string; __v?: number }> {
321
385
  }
322
386
  }
323
387
 
388
+ async findByIdNoIsolation(appuser: UserContext, id: string) {
389
+ if (this.hooks.beforeFetchRecord)
390
+ await this.hooks.beforeFetchRecord(appuser, id);
391
+
392
+ const data = await this.searchNoIsolation(appuser, { _id: id as any });
393
+ if (this.hooks.afterFetchRecord)
394
+ await this.hooks.afterFetchRecord(appuser, data[0]);
395
+
396
+ if (data.length == 1) {
397
+ // console.log('data0', data[0]);
398
+ return data[0];
399
+ } else {
400
+ return null;
401
+ }
402
+ }
403
+
404
+ async createManyWithId(appuser: UserContext, datas: T[]) {
405
+ if (Array.isArray(datas)) {
406
+ for (let i = 0; i < datas.length; i++) {
407
+ const data = datas[i];
408
+ let isolationFilter: any = { ...appuser.getCreateFilterWithId() };
409
+ isolationFilter = this.polishIsolationFilter(isolationFilter, data);
410
+
411
+ Object.assign(data, isolationFilter);
412
+ this.reCalculateValue(data);
413
+ await this.validateData(appuser, data);
414
+ this.applyNestedDateTime(appuser, data, 'create');
415
+ }
416
+
417
+ const result = await this.doc.insertMany(datas);
418
+ await this.audittrail.addManyEvents(
419
+ appuser,
420
+ this.documentName,
421
+ 'createMany',
422
+ datas,
423
+ );
424
+ return result;
425
+ } else {
426
+ throw new BadRequestException(
427
+ this.getDocumentType() + ': create many only support array',
428
+ );
429
+ }
430
+ }
431
+
432
+ /**
433
+ * create many from array with hooks (temporary used for generate sample data only)
434
+ * @param appuser
435
+ * @param datas
436
+ */
437
+ async createManyWithHook(appuser: UserContext, datas: T[]) {
438
+ if (Array.isArray(datas)) {
439
+ for (let i = 0; i < datas.length; i++) {
440
+ const data = datas[i];
441
+
442
+ let result;
443
+ const dbsession = appuser.getDBSession();
444
+ if (dbsession && !dbsession.inTransaction()) {
445
+ dbsession.startTransaction();
446
+ }
447
+
448
+ if (this.withDocNumberFormat && !data[this.documentIdentityCode]) {
449
+ await this.genNewDocNo(appuser, data);
450
+ }
451
+
452
+ let isolationFilter: any = { ...appuser.getCreateFilterWithId() };
453
+ isolationFilter = this.polishIsolationFilter(isolationFilter, data);
454
+
455
+ Object.assign(data, isolationFilter);
456
+ this.reCalculateValue(data);
457
+ await this.validateData(appuser, data);
458
+ this.applyNestedDateTime(appuser, data, 'create');
459
+ if (this.hooks.beforeCreate)
460
+ await this.hooks.beforeCreate(appuser, data);
461
+ const newdoc = new this.doc(data);
462
+
463
+ try {
464
+ result = await newdoc.save({ session: dbsession });
465
+ await this.audittrail.addEvent(
466
+ appuser,
467
+ this.documentName,
468
+ result._id,
469
+ 'create',
470
+ data,
471
+ );
472
+ appuser.addInsertedRecordId(this.documentName, result._id);
473
+ } catch (err) {
474
+ this.logger.error(err);
475
+ const processdata = await this.runWorker(
476
+ appuser,
477
+ 'processdata.processError',
478
+ {
479
+ err: err,
480
+ },
481
+ );
482
+
483
+ if (!processdata) {
484
+ throw new InternalServerErrorException(err);
485
+ } else {
486
+ throw new CustomException(processdata.code, processdata.msg);
487
+ }
488
+ }
489
+
490
+ try {
491
+ if (this.hooks.afterCreate)
492
+ await this.hooks.afterCreate(appuser, result);
493
+ this.callWebhook(appuser, 'create', result);
494
+
495
+ // return result as T;
496
+ } catch (err) {
497
+ throw new InternalServerErrorException(
498
+ `createHook ${this.documentType} error: ${JSON.stringify(err)}`,
499
+ `${this.documentType} createHook error`,
500
+ );
501
+ }
502
+ }
503
+ } else {
504
+ throw new BadRequestException(
505
+ this.getDocumentType() + ': create many only support array',
506
+ );
507
+ }
508
+ return 'ok';
509
+ }
510
+
324
511
  /**
325
512
  * create many from array, for performance reason it submit all item in 1 go, so it won't implement hooks
326
513
  * @param appuser
@@ -353,8 +540,7 @@ export class SimpleAppService<T extends { _id?: string; __v?: number }> {
353
540
  );
354
541
  }
355
542
  }
356
-
357
- async create(appuser: UserContext, data: T) {
543
+ async createWithId(appuser: UserContext, data: T) {
358
544
  let result;
359
545
 
360
546
  if (!data._id) {
@@ -376,7 +562,7 @@ export class SimpleAppService<T extends { _id?: string; __v?: number }> {
376
562
  await this.genNewDocNo(appuser, data);
377
563
  }
378
564
 
379
- let isolationFilter: any = { ...appuser.getCreateFilter() };
565
+ let isolationFilter: any = { ...appuser.getCreateFilterWithId() };
380
566
  isolationFilter = this.polishIsolationFilter(isolationFilter, data);
381
567
 
382
568
  this.logger.debug('isolationFilter', 'SimpleAppService');
@@ -405,13 +591,103 @@ export class SimpleAppService<T extends { _id?: string; __v?: number }> {
405
591
  appuser.addInsertedRecordId(this.documentName, result._id);
406
592
  } catch (err) {
407
593
  this.logger.error(err);
408
- throw new InternalServerErrorException(err);
594
+ const processdata = await this.runWorker(
595
+ appuser,
596
+ 'processdata.processError',
597
+ {
598
+ err: err,
599
+ },
600
+ );
601
+
602
+ if (!processdata) {
603
+ throw new InternalServerErrorException(err);
604
+ } else {
605
+ throw new CustomException(processdata.code, processdata.msg);
606
+ }
607
+ }
608
+
609
+ try {
610
+ if (this.hooks.afterCreate) await this.hooks.afterCreate(appuser, result);
611
+ this.callWebhook(appuser, 'create', result);
612
+ return result as T;
613
+ } catch (err) {
614
+ throw new InternalServerErrorException(
615
+ `createHook ${this.documentType} error: ${JSON.stringify(err)}`,
616
+ `${this.documentType} createHook error`,
617
+ );
618
+ }
619
+ }
620
+
621
+ async create(
622
+ appuser: UserContext,
623
+ data: T,
624
+ noStartTransaction: boolean = false,
625
+ ) {
626
+ let result;
627
+
628
+ if (!data._id) {
629
+ data._id = crypto.randomUUID();
630
+ }
631
+ const dbsession = appuser.getDBSession();
632
+ if (dbsession && !dbsession.inTransaction() && !noStartTransaction) {
633
+ dbsession.startTransaction();
634
+ }
635
+
636
+ this.logger.debug(
637
+ 'this.withDocNumberFormat :' +
638
+ this.withDocNumberFormat +
639
+ ' && ' +
640
+ '!data[this.documentIdentityCode] ==' +
641
+ !data[this.documentIdentityCode],
642
+ );
643
+ if (this.withDocNumberFormat && !data[this.documentIdentityCode]) {
644
+ await this.genNewDocNo(appuser, data);
409
645
  }
410
646
 
411
- // this.doc.create(data)
412
- // this.doc
647
+ let isolationFilter: any = { ...appuser.getCreateFilter() };
648
+ isolationFilter = this.polishIsolationFilter(isolationFilter, data);
649
+
650
+ this.logger.debug('isolationFilter', 'SimpleAppService');
651
+ this.logger.debug(isolationFilter, 'SimpleAppService');
652
+ this.logger.debug('Create data before isolation', 'SimpleAppService');
653
+ this.logger.debug(data, 'SimpleAppService');
654
+ Object.assign(data, isolationFilter);
655
+ this.reCalculateValue(data);
656
+ await this.validateData(appuser, data);
657
+ this.logger.debug(data, `after create validation`);
658
+ this.applyNestedDateTime(appuser, data, 'create');
659
+
660
+ if (this.hooks.beforeCreate) await this.hooks.beforeCreate(appuser, data);
661
+ this.logger.debug(data, `Create Record ${this.documentName}`);
662
+ const newdoc = new this.doc(data);
663
+ await this.identifyForeignKeys(appuser, data);
664
+ try {
665
+ result = await newdoc.save({ session: dbsession });
666
+ await this.audittrail.addEvent(
667
+ appuser,
668
+ this.documentName,
669
+ result._id,
670
+ 'create',
671
+ data,
672
+ );
673
+ appuser.addInsertedRecordId(this.documentName, result._id);
674
+ } catch (err) {
675
+ this.logger.error(err);
676
+ const processdata = await this.runWorker(
677
+ appuser,
678
+ 'processdata.processError',
679
+ {
680
+ err: err,
681
+ },
682
+ );
683
+
684
+ if (!processdata) {
685
+ throw new InternalServerErrorException(err);
686
+ } else {
687
+ throw new CustomException(processdata.code, processdata.msg);
688
+ }
689
+ }
413
690
 
414
- // result = await newdoc.save()
415
691
  try {
416
692
  if (this.hooks.afterCreate) await this.hooks.afterCreate(appuser, result);
417
693
  this.callWebhook(appuser, 'create', result);
@@ -459,8 +735,20 @@ export class SimpleAppService<T extends { _id?: string; __v?: number }> {
459
735
  }
460
736
  };
461
737
  async validateData(appuser: UserContext, data: T, _id?: string) {
462
- const ajv = getValidateService();
463
-
738
+ // console.log('validatedata');
739
+ const ajv = new Ajv({ allErrors: true, useDefaults: true });
740
+ addFormats(ajv);
741
+ addErrors(ajv);
742
+
743
+ // ajv.addFormat('tel', /^$|^\d{7,15}$/gm);
744
+ ajv.addFormat('tel', /.*$/);
745
+ ajv.addFormat('text', /.*$/);
746
+ ajv.addFormat('html', /.*$/);
747
+ ajv.addFormat('documentno', /.*$/);
748
+ ajv.addFormat('money', /.*$/);
749
+
750
+ ajv.addKeyword({ keyword: 'x-foreignkey', schemaType: 'string' });
751
+ ajv.addKeyword({ keyword: 'x-simpleapp-config', schemaType: 'object' });
464
752
  this.logger.debug('run hook during validation');
465
753
  let issuccess = true;
466
754
  if (this.hooks.beforeValidation) {
@@ -488,12 +776,14 @@ export class SimpleAppService<T extends { _id?: string; __v?: number }> {
488
776
  }
489
777
  const valid = validate(data);
490
778
  if (!valid) {
779
+ this.logger.error(JSON.stringify(data));
491
780
  this.logger.error(JSON.stringify(validate.errors), 'validate errors:');
492
781
  throw new BadRequestException(
493
782
  'Data validation failed',
494
783
  validate.errors as HttpExceptionOptions,
495
784
  );
496
785
  }
786
+ //no check for duplicate those
497
787
  if (this.hooks.afterValidation)
498
788
  await this.hooks.afterValidation(appuser, data, _id);
499
789
  }
@@ -543,7 +833,7 @@ export class SimpleAppService<T extends { _id?: string; __v?: number }> {
543
833
  dependency = await this.getRelatedRecords(appuser, id);
544
834
  //console.log('dependency', dependency);
545
835
  if (!dependency) {
546
- let filterIsolation = this.getIsolationFilter(appuser);
836
+ const filterIsolation = this.getIsolationFilter(appuser);
547
837
  this.polishIsolationFilter(filterIsolation);
548
838
 
549
839
  filterIsolation['_id'] = id;
@@ -578,11 +868,17 @@ export class SimpleAppService<T extends { _id?: string; __v?: number }> {
578
868
  } else {
579
869
  this.logger.debug('reject query', dependency);
580
870
 
581
- throw new ForbiddenException(dependency, 'Foreignkey constraint');
871
+ throw new ForbiddenException(
872
+ `This system detected that the [${this.documentName}] data has been used in other record`,
873
+ 'Foreignkey constraint',
874
+ );
582
875
  }
583
876
  } catch (err) {
584
- throw new InternalServerErrorException(err);
585
- //JSON.stringify(dependency),JSON.stringify(dependency)
877
+ if (err instanceof ForbiddenException) {
878
+ throw new ForbiddenException(err);
879
+ } else {
880
+ throw new InternalServerErrorException(err);
881
+ } //JSON.stringify(dependency),JSON.stringify(dependency)
586
882
  }
587
883
  }
588
884
 
@@ -590,7 +886,12 @@ export class SimpleAppService<T extends { _id?: string; __v?: number }> {
590
886
  // this.doc.updateOne(data);
591
887
  // };
592
888
 
593
- findIdThenUpdate = async (appuser: UserContext, id: string, data: T) => {
889
+ findIdThenUpdate = async (
890
+ appuser: UserContext,
891
+ id: string,
892
+ data: T,
893
+ noStartTransaction: boolean = false,
894
+ ) => {
594
895
  try {
595
896
  //version exists, need ensure different only 1
596
897
  const existingdata = await this.findById(appuser, id);
@@ -619,7 +920,7 @@ export class SimpleAppService<T extends { _id?: string; __v?: number }> {
619
920
  await this.hooks.beforeUpdate(appuser, id, existingdata, data);
620
921
 
621
922
  const dbsession = appuser.getDBSession();
622
- if (dbsession && !dbsession.inTransaction()) {
923
+ if (dbsession && !dbsession.inTransaction() && !noStartTransaction) {
623
924
  dbsession.startTransaction();
624
925
  }
625
926
  // try {
@@ -660,6 +961,80 @@ export class SimpleAppService<T extends { _id?: string; __v?: number }> {
660
961
  }
661
962
  };
662
963
 
964
+ findIdThenUpdateNoIsolation = async (
965
+ appuser: UserContext,
966
+ id: string,
967
+ data: T,
968
+ ) => {
969
+ try {
970
+ //version exists, need ensure different only 1
971
+ const existingdata = await this.findByIdNoIsolation(appuser, id);
972
+ if (!existingdata)
973
+ throw new NotFoundException(
974
+ `${this.documentName} findIdThenUpdate: _id:${id} not found`,
975
+ 'not found',
976
+ );
977
+
978
+ this.logger.debug(
979
+ 'update id:' + id,
980
+ this.documentName + ' findIdThenUpdate',
981
+ );
982
+ if (typeof data.__v == 'number' && data.__v != existingdata.__v) {
983
+ throw new BadRequestException(
984
+ `You submit older version data "v${data.__v}"" but latest version = "v${existingdata.__v}"`,
985
+ );
986
+ }
987
+ // this.logger.debug('warn1', existingdata);
988
+ data.__v = existingdata.__v + 1;
989
+ // if (!existingdata) {
990
+ // throw new NotFoundException(`${id} not found`, 'not found');
991
+ // }
992
+ // this.logger.debug('warn2');
993
+ if (this.hooks.beforeUpdate)
994
+ await this.hooks.beforeUpdate(appuser, id, existingdata, data);
995
+
996
+ const dbsession = appuser.getDBSession();
997
+ if (dbsession && !dbsession.inTransaction()) {
998
+ dbsession.startTransaction();
999
+ }
1000
+ // try {
1001
+ // Object.assign(data, appuser.getUpdateFilter());
1002
+ // Object.assign(existingdata, data);
1003
+
1004
+ delete data['_id'];
1005
+ this.reCalculateValue(data);
1006
+
1007
+ // existingdata['_id']=''
1008
+ await this.validateData(appuser, data, id);
1009
+
1010
+ const isolationFilter = {};
1011
+ // this.polishIsolationFilter(isolationFilter);
1012
+ isolationFilter['_id'] = id;
1013
+ this.applyNestedDateTime(appuser, data, 'update');
1014
+
1015
+ const result = await this.doc.findOneAndReplace(isolationFilter, data, {
1016
+ session: dbsession,
1017
+ new: true,
1018
+ });
1019
+ await this.audittrail.addEvent(
1020
+ appuser,
1021
+ this.documentName,
1022
+ id,
1023
+ 'update',
1024
+ data,
1025
+ );
1026
+
1027
+ appuser.addUpdatedRecordId(this.documentName, data._id);
1028
+ if (this.hooks.afterUpdate)
1029
+ await this.hooks.afterUpdate(appuser, id, existingdata, result);
1030
+ this.callWebhook(appuser, 'update', result);
1031
+ return result; // await this.findById(appuser, id);
1032
+ } catch (err) {
1033
+ this.logger.error(err);
1034
+ throw new InternalServerErrorException(err.message);
1035
+ }
1036
+ };
1037
+
663
1038
  findIdThenPatch = async (
664
1039
  appuser: UserContext,
665
1040
  id: string,
@@ -752,13 +1127,16 @@ export class SimpleAppService<T extends { _id?: string; __v?: number }> {
752
1127
  for (let j = 0; j < fobjs.length; j++) {
753
1128
  const fkey = fobjs[j];
754
1129
  //not deleted in current session, check from database
755
- let filter = {};
1130
+ const filter = {};
756
1131
  filter[fkey] = id;
757
1132
  const result = await collection.findOne(filter, {
758
1133
  session: appuser.getDBSession(),
759
1134
  });
760
1135
  if (result) {
761
- this.logger.error(result, 'related result found');
1136
+ this.logger.error(
1137
+ result,
1138
+ `related result found in ${collectionname} ${fkey} = ${id}`,
1139
+ );
762
1140
  return result;
763
1141
  }
764
1142
  }
@@ -948,7 +1326,7 @@ export class SimpleAppService<T extends { _id?: string; __v?: number }> {
948
1326
 
949
1327
  keystore[collectionname] = results;
950
1328
  //console.log("keystorekeystore",keystore)
951
- let addfield = { $addFields: { collection: collectionname } };
1329
+ const addfield = { $addFields: { collection: collectionname } };
952
1330
 
953
1331
  const stagefilter: PipelineStage = {
954
1332
  $unionWith: {
@@ -974,7 +1352,7 @@ export class SimpleAppService<T extends { _id?: string; __v?: number }> {
974
1352
  pipelines as HttpExceptionOptions,
975
1353
  );
976
1354
  } else {
977
- let searchresult: any = {};
1355
+ const searchresult: any = {};
978
1356
  unionresult.forEach((item) => {
979
1357
  if (searchresult[item.collection]) {
980
1358
  searchresult[item.collection].push(item._id);
@@ -1024,6 +1402,23 @@ export class SimpleAppService<T extends { _id?: string; __v?: number }> {
1024
1402
  return pdfresult;
1025
1403
  }
1026
1404
 
1405
+ async checkMultipleUnionExist(
1406
+ appuser: UserContext,
1407
+ data: string[],
1408
+ ): Promise<CheckMutipleUnionExistResponse> {
1409
+ const response: CheckMutipleUnionExistResponse = [];
1410
+ const unionKey = this.getDocumentIdentityCode();
1411
+ const searchQuery: any = { [unionKey]: { $in: data } };
1412
+ // search for multiple union exist
1413
+ const result = await this.search(appuser, searchQuery);
1414
+ for (const item of data) {
1415
+ const found = result.find((x) => x[unionKey] === item);
1416
+ response.push({ [item]: found ? 1 : 0 });
1417
+ }
1418
+
1419
+ return response;
1420
+ }
1421
+
1027
1422
  searchToAggregate(
1028
1423
  appuser: UserContext,
1029
1424
  filter: FilterQuery<T>,
@@ -1046,7 +1441,7 @@ export class SimpleAppService<T extends { _id?: string; __v?: number }> {
1046
1441
  collections.forEach((tokey: string) => {
1047
1442
  const toarr = tokey.split('.');
1048
1443
  const to = toarr[0];
1049
- toarr.splice(0,1)
1444
+ toarr.splice(0, 1);
1050
1445
  const foreignField = toarr.join('.') ?? '_id';
1051
1446
  pipelines.push({
1052
1447
  $lookup: {
@@ -1057,7 +1452,9 @@ export class SimpleAppService<T extends { _id?: string; __v?: number }> {
1057
1452
  pipeline: [{ $match: { tenantId: appuser.getTenantId() } }],
1058
1453
  },
1059
1454
  });
1060
- pipelines.push({ $unwind: '$_' + to });
1455
+
1456
+ // for support multiple data on lookup
1457
+ // pipelines.push({ $unwind: '$_' + to });
1061
1458
 
1062
1459
  if (Object.keys(projection).length > 0) projection['_' + to] = 1;
1063
1460
  });
@@ -1,6 +1,6 @@
1
1
  /**
2
- * This file was automatically generated by simpleapp generator. Every
3
- * MODIFICATION OVERRIDE BY GENERATEOR
2
+ * This file was automatically generated by simpleapp generator. It is changable.
3
+ * --remove-this-line-to-prevent-override--
4
4
  * last change 2024-02-23
5
5
  * Author: Ks Tan
6
6
  */
@@ -1,6 +1,6 @@
1
1
  /**
2
- * This file was automatically generated by simpleapp generator. Every
3
- * MODIFICATION OVERRIDE BY GENERATEOR
2
+ * This file was automatically generated by simpleapp generator. It is changable.
3
+ * --remove-this-line-to-prevent-override--
4
4
  * last change 2024-02-23
5
5
  * Author: Ks Tan
6
6
  */