@simitgroup/simpleapp-generator 1.6.2-alpha → 1.6.4-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 (161) hide show
  1. package/README.md +6 -5
  2. package/dist/buildinschemas/branch.d.ts.map +1 -1
  3. package/dist/buildinschemas/branch.js +1 -2
  4. package/dist/buildinschemas/branch.js.map +1 -1
  5. package/dist/buildinschemas/changehistories.d.ts +3 -0
  6. package/dist/buildinschemas/changehistories.d.ts.map +1 -0
  7. package/dist/buildinschemas/changehistories.js +36 -0
  8. package/dist/buildinschemas/changehistories.js.map +1 -0
  9. package/dist/buildinschemas/index.d.ts +1 -0
  10. package/dist/buildinschemas/index.d.ts.map +1 -1
  11. package/dist/buildinschemas/index.js +3 -1
  12. package/dist/buildinschemas/index.js.map +1 -1
  13. package/dist/buildinschemas/organization.js +2 -2
  14. package/dist/buildinschemas/organization.js.map +1 -1
  15. package/dist/buildinschemas/user.d.ts.map +1 -1
  16. package/dist/buildinschemas/user.js +5 -1
  17. package/dist/buildinschemas/user.js.map +1 -1
  18. package/dist/buildinschemas/webhook.d.ts +3 -0
  19. package/dist/buildinschemas/webhook.d.ts.map +1 -0
  20. package/dist/buildinschemas/webhook.js +33 -0
  21. package/dist/buildinschemas/webhook.js.map +1 -0
  22. package/dist/framework.d.ts.map +1 -1
  23. package/dist/framework.js +3 -2
  24. package/dist/framework.js.map +1 -1
  25. package/dist/generate.js +30 -11
  26. package/dist/generate.js.map +1 -1
  27. package/dist/index.js +4 -1
  28. package/dist/index.js.map +1 -1
  29. package/dist/processors/jsonschemabuilder.d.ts.map +1 -1
  30. package/dist/processors/jsonschemabuilder.js +10 -2
  31. package/dist/processors/jsonschemabuilder.js.map +1 -1
  32. package/dist/type.d.ts +2 -0
  33. package/dist/type.d.ts.map +1 -1
  34. package/package.json +1 -1
  35. package/src/buildinschemas/branch.ts +1 -2
  36. package/src/buildinschemas/changehistories.ts +33 -0
  37. package/src/buildinschemas/index.ts +2 -1
  38. package/src/buildinschemas/organization.ts +2 -2
  39. package/src/buildinschemas/user.ts +5 -1
  40. package/src/buildinschemas/webhook.ts +31 -0
  41. package/src/framework.ts +3 -2
  42. package/src/generate.ts +35 -15
  43. package/src/index.ts +8 -3
  44. package/src/processors/jsonschemabuilder.ts +10 -2
  45. package/src/type.ts +2 -0
  46. package/templates/basic/nest/controller.ts.eta +23 -2
  47. package/templates/basic/nest/model.ts.eta +9 -1
  48. package/templates/basic/nest/resolver.ts.eta +2 -2
  49. package/templates/basic/nuxt/pages.[id].vue.eta +7 -7
  50. package/templates/basic/nuxt/pages.form.vue.eta +3 -6
  51. package/templates/basic/nuxt/pages.landing.vue.eta +2 -21
  52. package/templates/basic/nuxt/simpleapp.generate.client.ts.eta +8 -1
  53. package/templates/nest/src/simpleapp/generate/apischemas/simpleapp.apischema.ts.eta +2 -0
  54. package/templates/nest/src/simpleapp/generate/commons/dicts/documents.ts.eta +9 -2
  55. package/templates/nest/src/simpleapp/generate/commons/docnogenerator.service.ts.eta +36 -30
  56. package/templates/nest/src/simpleapp/generate/commons/roles/roles.enum.ts.eta +5 -10
  57. package/templates/nest/src/simpleapp/generate/commons/roles/roles.group.ts.eta +1 -0
  58. package/templates/nest/src/simpleapp/generate/commons/runwebhook.service.ts.eta +50 -0
  59. package/templates/nest/src/simpleapp/generate/commons/user.context.ts.eta +13 -3
  60. package/templates/nest/src/simpleapp/generate/controllers/simpleapp.controller.ts.eta +9 -1
  61. package/templates/nest/src/simpleapp/generate/processors/branch.processor.ts.eta +12 -6
  62. package/templates/nest/src/simpleapp/generate/processors/simpleapp.processor.ts.eta +128 -14
  63. package/templates/nest/src/simpleapp/generate/types/schema.type.ts.eta +3 -1
  64. package/templates/nest/src/simpleapp/generate/types/simpleapp.type.ts.eta +1 -0
  65. package/templates/nest/src/simpleapp/profile/profile.controller.ts.eta +19 -0
  66. package/templates/nest/src/simpleapp/profile/profile.service.ts.eta +30 -8
  67. package/templates/nest/src/simpleapp/simpleapp.module.ts.eta +2 -1
  68. package/templates/nuxt/{app.vue._eta → app.vue.eta} +3 -1
  69. package/templates/nuxt/assets/css/calendar.css._eta +3 -0
  70. package/templates/nuxt/assets/css/style.css._eta +1 -1
  71. package/templates/nuxt/assets/images/unknown.png.eta +0 -0
  72. package/templates/nuxt/assets/primevue/passthrough.ts._eta +6 -1
  73. package/templates/nuxt/components/button/ButtonAction.vue._eta +49 -7
  74. package/templates/nuxt/components/button/ButtonDanger.vue._eta +11 -3
  75. package/templates/nuxt/components/button/ButtonDefault.vue._eta +11 -3
  76. package/templates/nuxt/components/button/ButtonPrimary.vue._eta +9 -3
  77. package/templates/nuxt/components/button/ButtonSecondary.vue._eta +33 -0
  78. package/templates/nuxt/components/button/ButtonText.vue._eta +9 -5
  79. package/templates/nuxt/components/button/ButtonWarning.vue._eta +11 -3
  80. package/templates/nuxt/components/calendar/CalendarInput.vue.eta +17 -14
  81. package/templates/nuxt/components/calendar/CalendarSmall.vue.eta +33 -16
  82. package/templates/nuxt/components/chart/card.vue._eta +1 -1
  83. package/templates/nuxt/components/debug/DebugDocumentData.vue.eta +36 -26
  84. package/templates/nuxt/components/event/EventDocumentViewer.vue._eta +62 -22
  85. package/templates/nuxt/components/form/FormBranch.vue._eta +52 -5
  86. package/templates/nuxt/components/form/FormDocnoformat.vue.eta +14 -10
  87. package/templates/nuxt/components/form/FormUser.vue._eta +1 -1
  88. package/templates/nuxt/components/form/user/FormUserPermission.vue.eta +77 -59
  89. package/templates/nuxt/components/header/HeaderSelectBranch.vue.eta +42 -35
  90. package/templates/nuxt/components/image/ImageAvatar.vue.eta._vue +30 -0
  91. package/templates/nuxt/components/image/ImageOrganization.vue.eta.vue +7 -5
  92. package/templates/nuxt/components/image/ImageToBase64Uploader.vue.eta.vue +67 -50
  93. package/templates/nuxt/components/list/ListDocument.vue.eta +10 -5
  94. package/templates/nuxt/components/list/ListDocumentTable.vue.eta +21 -13
  95. package/templates/nuxt/components/list/ListMessages.vue.eta +1 -1
  96. package/templates/nuxt/components/list/ListView.vue.eta +94 -56
  97. package/templates/nuxt/components/overlay/OverlayPanelWithToolBar.vue.eta +11 -4
  98. package/templates/nuxt/components/overlay/OverlaySideBarCrud.vue.eta +17 -6
  99. package/templates/nuxt/components/overlay/OverlayViewer.vue.eta +16 -6
  100. package/templates/nuxt/components/page/PageDocList.vue.eta +108 -31
  101. package/templates/nuxt/components/renderer/RendererDate.vue.eta +8 -2
  102. package/templates/nuxt/components/renderer/RendererDateTime.vue.eta +7 -1
  103. package/templates/nuxt/components/renderer/RendererDocHistories.vue.eta +56 -0
  104. package/templates/nuxt/components/renderer/RendererForeignKey.vue.eta +9 -8
  105. package/templates/nuxt/components/renderer/RendererLink.vue.eta +7 -4
  106. package/templates/nuxt/components/renderer/RendererMoney.vue.eta +25 -17
  107. package/templates/nuxt/components/renderer/RendererTime.vue.eta +7 -1
  108. package/templates/nuxt/components/renderer/RendererViewer.vue.eta +19 -9
  109. package/templates/nuxt/components/select/SelectTemplate.vue.eta +76 -38
  110. package/templates/nuxt/components/session/SessionBlock.vue.eta +44 -46
  111. package/templates/nuxt/components/simpleApp/SimpleAppAutocomplete.vue.eta +61 -24
  112. package/templates/nuxt/components/simpleApp/SimpleAppCalendarInput.vue.eta +64 -0
  113. package/templates/nuxt/components/simpleApp/SimpleAppChildrenList.vue.eta +26 -14
  114. package/templates/nuxt/components/simpleApp/SimpleAppDocumentNo.vue.eta +8 -8
  115. package/templates/nuxt/components/simpleApp/SimpleAppFieldContainer.vue.eta +1 -1
  116. package/templates/nuxt/components/simpleApp/SimpleAppFormToolBar.vue._eta +128 -33
  117. package/templates/nuxt/components/simpleApp/SimpleAppInput.vue.eta +89 -168
  118. package/templates/nuxt/components/simpleApp/SimpleAppInputTable.vue.eta +43 -40
  119. package/templates/nuxt/components/simpleApp/SimpleAppUserPicker.vue.eta +387 -0
  120. package/templates/nuxt/components/text/TextDocStatus.vue._eta +22 -0
  121. package/templates/nuxt/components/user/UserButtonCreateTenant.vue._eta +13 -15
  122. package/templates/nuxt/components/user/UserButtonPermissionInfo.vue.eta +127 -93
  123. package/templates/nuxt/components/user/UserTenantPicker.vue.eta +1 -1
  124. package/templates/nuxt/composables/confirm.generate.ts.eta +19 -0
  125. package/templates/nuxt/composables/date.generate.ts.eta +106 -8
  126. package/templates/nuxt/composables/getDocument.generate.ts.eta +8 -6
  127. package/templates/nuxt/composables/getOpenApi.generate.ts.eta +58 -10
  128. package/templates/nuxt/composables/getUserStore.generate.ts.eta +37 -5
  129. package/templates/nuxt/composables/goTo.generate.ts.eta +14 -1
  130. package/templates/nuxt/composables/graphquery.generate.ts.eta +20 -2
  131. package/templates/nuxt/composables/recently.generate.ts.eta +16 -0
  132. package/templates/nuxt/composables/roles.generate.ts.eta +8 -13
  133. package/templates/nuxt/composables/stringHelper.generate.ts.eta +54 -1
  134. package/templates/nuxt/composables/sysmessage.generate.ts.eta +1 -1
  135. package/templates/nuxt/pages/[xorg]/docnoformat.vue.eta +1 -1
  136. package/templates/nuxt/pages/[xorg]/mobile/docnoformat/{index.vue.eta → index.vue.etaxxx} +1 -1
  137. package/templates/nuxt/pages/[xorg]/mobile/user/{index.vue.eta → index.vue.etaxxx} +1 -1
  138. package/templates/nuxt/pages/[xorg]/{organization.vue.eta → organization.vue._eta} +39 -10
  139. package/templates/nuxt/pages/[xorg]/profile.vue.eta +1 -1
  140. package/templates/nuxt/pages/[xorg]/user.vue.eta +13 -10
  141. package/templates/nuxt/pages/login.vue._eta +4 -1
  142. package/templates/nuxt/plugins/10.simpleapp-event.ts.eta +4 -0
  143. package/templates/nuxt/plugins/20.simpleapp-userstore.ts.eta +45 -26
  144. package/templates/nuxt/plugins/70.recently.ts.eta +55 -0
  145. package/templates/nuxt/providers/my-provider.ts.eta +22 -0
  146. package/templates/nuxt/server/api/[xorg]/{[...].ts.eta → [...].ts._eta} +47 -21
  147. package/templates/nuxt/simpleapp/generate/clients/SimpleAppClient.ts.eta +44 -3
  148. package/templates/nuxt/types/events.ts.eta +3 -2
  149. package/templates/nuxt/types/others.ts.eta +11 -1
  150. package/templates/nuxt/types/schema.ts.eta +3 -1
  151. package/templates/nuxt/types/simpleappinput.ts.eta +1 -1
  152. package/templates/nuxt/types/user.ts.eta +8 -7
  153. package/templates/project/jsonschemas/branch.json._eta +1 -0
  154. package/templates/project/jsonschemas/invoice.json._eta +4 -3
  155. package/templates/project/jsonschemas/organization.json._eta +2 -2
  156. package/templates/project/lang/default._json +5 -1
  157. package/tsconfig.tsbuildinfo +1 -1
  158. package/templates/nuxt/components/image/ImageAvatar.vue.eta.vue +0 -38
  159. /package/templates/nuxt/pages/[xorg]/mobile/{index.vue._eta → index.vue._etaxxx} +0 -0
  160. /package/templates/nuxt/pages/[xorg]/mobile/organization/{[id].vue._eta → [id].vue._etaxxx} +0 -0
  161. /package/templates/nuxt/pages/[xorg]/mobile/{pickgroup.vue._eta → pickgroup.vue._etaxxx} +0 -0
@@ -7,39 +7,39 @@
7
7
  import { UserContext } from './user.context';
8
8
  import { InjectModel } from '@nestjs/mongoose';
9
9
  import { Model } from 'mongoose';
10
- import {DocNumberFormatResult} from '../types'
11
- import { Docnoformat } from '../types/docno.type'
12
- import { Injectable, InternalServerErrorException,BadRequestException } from '@nestjs/common';
13
- import moment from 'moment';
14
- import {ForeignKey} from '../types'
10
+ import { DocNumberFormatResult } from '../types';
11
+ import { Docnoformat } from '../types/docno.type';
12
+ import { Injectable, InternalServerErrorException, BadRequestException} from '@nestjs/common';
13
+ // import moment from 'moment';
14
+ import dayjs from 'dayjs'
15
+ import { ForeignKey } from '../types';
15
16
 
16
- export class DocNumberFormatGenerator{
17
- constructor(@InjectModel('Docnoformat') private docformat: Model<Docnoformat>) {}
17
+ export class DocNumberFormatGenerator {
18
+ constructor(@InjectModel('Docnoformat') private docformat: Model<Docnoformat>,) {}
18
19
 
19
20
 
20
- async generateNextNumberFromDocument(appuser:UserContext,docType:string,data:any){
21
- let formatId = ''
22
- if(data.docNoFormat && data.docNoFormat._id){
23
- formatId=data.docNoFormat._id
21
+ async generateNextNumberFromDocument( appuser: UserContext, docType: string, data: any,) {
22
+ let formatId = '';
23
+ if (data.docNoFormat && data.docNoFormat._id) {
24
+ formatId = data.docNoFormat._id;
24
25
  }
25
- const docnoobj = await this.generateNextNo(appuser,docType,formatId)
26
- const result:ForeignKey = {
27
- _id : String(docnoobj.formatId),
28
- label: docnoobj.formatName
29
- }
30
- data.docNoFormat = result
31
- return docnoobj.result
26
+ const docnoobj = await this.generateNextNo(appuser, docType, formatId);
27
+ const result: ForeignKey = {
28
+ _id: String(docnoobj.formatId),
29
+ label: docnoobj.formatName,
30
+ };
31
+ data.docNoFormat = result;
32
+ return docnoobj.result;
32
33
  }
33
34
 
34
-
35
- generateNextNo = async (appuser: UserContext,doctype: string,id: string = '') => {
35
+ generateNextNo = async ( appuser: UserContext,doctype: string, id: string = '',) => {
36
36
  doctype = doctype.toUpperCase();
37
37
  let filter = { docNoType: doctype };
38
38
  if (id) {
39
39
  filter['_id'] = id;
40
40
  }
41
- Object.assign(filter,appuser.getBranchFilter())
42
- const result = await this.docformat.find(filter)
41
+ Object.assign(filter, appuser.getBranchFilter());
42
+ const result = await this.docformat.find(filter);
43
43
  //search(appuser, filter);
44
44
  if (result && result.length > 0) {
45
45
  const d: Docnoformat = result[0];
@@ -47,11 +47,17 @@ export class DocNumberFormatGenerator{
47
47
  const newdocno = DocNumberFormatGenerator.previewDocNo(d);
48
48
  const newnextnumber = d.nextNumber + 1;
49
49
  const updatedata = { nextNumber: newnextnumber } as Docnoformat;
50
- const updateresult = await this.docformat.findByIdAndUpdate(recordId,updatedata).session(appuser.getDBSession())
51
-
52
- if (updateresult) {
53
- const result:DocNumberFormatResult = {formatId:d._id,formatName:d.docNoFormatName,result: newdocno }
54
- return result
50
+ const updateresult = await this.docformat
51
+ .findByIdAndUpdate(recordId, updatedata)
52
+ .session(appuser.getDBSession());
53
+
54
+ if (updateresult) {
55
+ const result: DocNumberFormatResult = {
56
+ formatId: d._id,
57
+ formatName: d.docNoFormatName,
58
+ result: newdocno,
59
+ };
60
+ return result;
55
61
  //;
56
62
  } else {
57
63
  throw new InternalServerErrorException(
@@ -86,13 +92,13 @@ export class DocNumberFormatGenerator{
86
92
  nextnumber = '0' + nextnumber;
87
93
  }
88
94
  newvalue = newvalue.replace(numberpattern[0], nextnumber);
89
- }
95
+ }
90
96
 
91
97
  if (datepattern && datepattern.length > 0) {
92
98
  for (let d = 0; d < datepattern.length; d++) {
93
99
  const dpattern = datepattern[d];
94
100
  const date = new Date();
95
- const formatteddate = moment().format(
101
+ const formatteddate =dayjs().format(
96
102
  dpattern.replace('{', '').replace('}', ''),
97
103
  );
98
104
  newvalue = newvalue.replace(dpattern, formatteddate);
@@ -104,4 +110,4 @@ export class DocNumberFormatGenerator{
104
110
  throw new InternalServerErrorException(errors);
105
111
  }
106
112
  };
107
- }
113
+ }
@@ -11,6 +11,11 @@ export enum Role {
11
11
  SuperUser = 'suerpuser', //reserved, plan use by support
12
12
  TenantOwner = 'tenantowner', //tenant owner/creator
13
13
  User = 'user',
14
+ //role generated from groups
15
+ <% Object.getOwnPropertyNames(it.allroles).forEach((key)=>{%>
16
+ <%= capitalizeFirstLetter(key) %> = '<%=key%>',
17
+ <%})%>
18
+ //role generate from schema
14
19
  <% for(let i=0;i<it.modules.length; i++){ %>
15
20
  <% const m = it.modules[i] %>
16
21
  <%=m.docname%>_access='<%=m.docname%>:access',
@@ -26,17 +31,7 @@ export enum Role {
26
31
  <%if(m.schema['x-simpleapp-config']['printFormats']){%>
27
32
  <%=m.docname%>_print='<%=m.docname%>:print',
28
33
  <%}%>
29
-
30
-
31
- <% if(m.api && m.api.length >0) {%>
32
-
33
- <% for(let j=0;j<m.api.length; j++){ %>
34
- <%let api = m.api[j]%>
35
34
 
36
- <%=m.docname%>_<%=api.action%>='<%=m.docname%>:<%=api.action%>',
37
-
38
- <%}%>
39
- <%}%>
40
35
  <% if(m.schema['x-simpleapp-config']['allStatus'] && m.schema['x-simpleapp-config']['allStatus'].length >0) {%>
41
36
  <%let allstatus = m.schema['x-simpleapp-config']['allStatus']%>
42
37
  <% for(let j=0;j<allstatus.length; j++){ %>
@@ -9,6 +9,7 @@ import {Role} from './roles.enum'
9
9
  <% Object.getOwnPropertyNames(it.allroles).forEach((key)=>{ %>
10
10
  export const <%=key%>=()=>[
11
11
  Role.User,
12
+ Role.<%=capitalizeFirstLetter(key)%>,
12
13
  <%for(let i=0; i <it.allroles[key].length; i++){%>
13
14
  Role.<%=it.allroles[key][i]%>,
14
15
  <%}%>
@@ -0,0 +1,50 @@
1
+ import {
2
+ BadRequestException,
3
+ Injectable,
4
+ InternalServerErrorException,
5
+ Logger,
6
+ NotFoundException,
7
+ } from '@nestjs/common';
8
+ import { EventEmitter2, OnEvent } from '@nestjs/event-emitter';
9
+ import { UserContext } from './user.context';
10
+ import { WebhookService } from 'src/simpleapp/services/webhook.service';
11
+ @Injectable()
12
+ export class RunWebhookService {
13
+ public constructor(private webhookService: WebhookService) {}
14
+ @OnEvent('webhook')
15
+ async runWebhook(
16
+ appuser: UserContext,
17
+ documentName: string,
18
+ actionName: string,
19
+ data?: any,
20
+ ) {
21
+ let subscribeall=false
22
+ const webhooks = await this.webhookService.search(appuser, {
23
+ documentName: documentName,
24
+ active: true,
25
+ });
26
+ if (webhooks.length == 0) return;
27
+
28
+ const webhook = webhooks[0];
29
+ let subscribes:string[] = []
30
+ if(webhook.setting=='' || webhook.setting===undefined){
31
+ subscribeall=true
32
+ }else{
33
+ subscribes= JSON.parse(webhook.setting)
34
+ }
35
+
36
+ if(subscribes.includes(actionName)){
37
+ const webhookurl = webhook.url;
38
+ const secretkey = webhook.secret;
39
+ const req = await fetch(webhookurl, {
40
+ method: 'POST',
41
+ headers: { 'x-apiKey': secretkey },
42
+ body: JSON.stringify(data),
43
+ });
44
+ const statusCode = req.status
45
+ const body = req.body
46
+
47
+ }
48
+
49
+ }
50
+ }
@@ -91,6 +91,7 @@ export class UserContext {
91
91
  getTenantId = () => this.tenantId;
92
92
  getOrgId = () => this.orgId;
93
93
  getBranchId = () => this.branchId;
94
+ getBranchCode = () => this.branchCode;
94
95
  getEmail = () => this.email;
95
96
  getTimeZone = () => this.timeZone;
96
97
  getCountry = () => this.country;
@@ -218,7 +219,7 @@ export class UserContext {
218
219
  }
219
220
  }
220
221
 
221
- userinfo.branchRecordId = myperm.currentbranch[0].branchRecordId;
222
+ userinfo.branchRecordId = myperm.currentbranch[0]._id;
222
223
  userinfo.branchCode = myperm.currentbranch[0].branchCode;
223
224
  userinfo.branchName = myperm.currentbranch[0].branchName;
224
225
  userinfo.orgRecordId = myperm.currentorg[0]._id;
@@ -258,7 +259,7 @@ export class UserContext {
258
259
  const tokeninfo = jwt.decode(tokenstr);
259
260
  this.token = tokenstr;
260
261
  this.uid = tokeninfo?.sub ?? '';
261
- this.email = tokeninfo?.email ??'';
262
+ this.email = tokeninfo?.email ?? '';
262
263
  this.uname = tokeninfo?.preferred_username ?? '';
263
264
  this.fullname = tokeninfo?.name ?? [];
264
265
  this.ssoACL = tokeninfo?.resource_access ?? [];
@@ -535,7 +536,7 @@ export class UserContext {
535
536
  };
536
537
 
537
538
  searchInsertedRecordId(collection: string, _id: string) {
538
- if(!this.modifiedRecords.createds[collection]) return undefined
539
+ if (!this.modifiedRecords.createds[collection]) return undefined;
539
540
  return this.modifiedRecords.createds[collection].find(
540
541
  (item) => item === _id,
541
542
  );
@@ -724,4 +725,13 @@ export class UserContext {
724
725
  }
725
726
  return data;
726
727
  }
728
+
729
+ offsetDate(date: string): string {
730
+ const timestamp = new Date(date).getTime();
731
+ const offsets = this.getOffsetMinute() * 60000;
732
+ const isodate =
733
+ new Date(timestamp + offsets).toISOString().split('.')[0] + 'Z';
734
+ // console.log("Generate schedule ",date,":",appuser.getOffsetMinute()," => ",isodate)
735
+ return isodate;
736
+ }
727
737
  }
@@ -30,6 +30,7 @@ type ServiceType = {
30
30
  findIdThenPatch: Function;
31
31
  setData: Function;
32
32
  getAutoComplete: Function;
33
+ fullTextSearch:Function;
33
34
  };
34
35
 
35
36
  // @ApiTags(doctype)
@@ -50,15 +51,22 @@ export class SimpleAppAbstractController<
50
51
  async _list(appuser: UserContext) {
51
52
  return this.service.list(appuser);
52
53
  }
54
+ async _fulltextsearch(appuser: UserContext, keyword: string) {
55
+ return this.service.fullTextSearch(
56
+ appuser,
57
+ keyword
58
+ );
59
+ }
53
60
  async _search(appuser: UserContext, searchObject: SearchBody) {
54
61
  return this.service.search(
55
62
  appuser,
56
63
  searchObject['filter'],
57
64
  searchObject['fields'],
58
65
  searchObject['sorts'],
66
+ searchObject['lookup'],
59
67
  );
60
68
  }
61
- async _autocomplete(appuser: UserContext, keyword:string,data?:T) {
69
+ async _autocomplete(appuser: UserContext, keyword: string, data?: T) {
62
70
  return this.service.getAutoComplete(appuser, keyword, data);
63
71
  }
64
72
  async _findOne(appuser: UserContext, id: string) {
@@ -6,7 +6,11 @@
6
6
  */
7
7
  import { UserContext } from '../commons/user.context';
8
8
  import * as sharelibs from '../sharelibs';
9
- import { Injectable, Inject,InternalServerErrorException } from '@nestjs/common';
9
+ import {
10
+ Injectable,
11
+ Inject,
12
+ InternalServerErrorException,
13
+ } from '@nestjs/common';
10
14
  import { InjectModel } from '@nestjs/mongoose';
11
15
  import * as jsonpath from 'jsonpath';
12
16
  import { Model } from 'mongoose';
@@ -17,7 +21,7 @@ import { DocNumberFormatGenerator } from '../commons/docnogenerator.service';
17
21
  import { AutoincreamentService } from '../../services/autoinc.service';
18
22
  import { alldocuments } from '../commons/dicts/documents';
19
23
  import { Docnoformat, DocnoformatService } from '../../services/docno.service';
20
- import { BranchOrganization, Branch,BranchHooks } from '../types/branch.type';
24
+ import { BranchOrganization, Branch, BranchHooks } from '../types/branch.type';
21
25
  import {
22
26
  DefaultBranchOrganization,
23
27
  DefaultBranch,
@@ -32,9 +36,11 @@ export class BranchProcessor extends SimpleAppService<Branch> {
32
36
  protected strictIsolation = false;
33
37
  protected documentIdentityCode = 'branchCode';
34
38
  protected documentIdentityLabel = 'branchName';
35
- protected hooks: BranchHooks = {
36
- beforeCreate: async (appuser: UserContext, data: Branch) => await this.branchBeforeCreate(appuser,data),
37
- afterCreate: async (appuser: UserContext, data: Branch) => await this.branchAfterCreate(appuser,data),
39
+ protected hooks: BranchHooks = {
40
+ beforeCreate: async (appuser: UserContext, data: Branch) =>
41
+ await this.branchBeforeCreate(appuser, data),
42
+ afterCreate: async (appuser: UserContext, data: Branch) =>
43
+ await this.branchAfterCreate(appuser, data),
38
44
  };
39
45
  protected foreignkeys = { organization: ['$.organization._id'] };
40
46
  constructor(mydoc: Model<Branch>) {
@@ -74,7 +80,7 @@ export class BranchProcessor extends SimpleAppService<Branch> {
74
80
  const docformats = alldocuments.filter((item) => item.docNumber);
75
81
  for (let i = 0; i < docformats.length; i++) {
76
82
  const doc = docformats[i];
77
- const pattern = `${doc.docType}-${branchCode}-[00000]`;
83
+ const pattern = doc.docNoPattern.replace('@BranchCode',branchCode);
78
84
  const formatdata: Docnoformat = {
79
85
  _id: crypto.randomUUID(),
80
86
  docNoFormatNo: `${doc.docType}-${branchCode}`,
@@ -43,7 +43,7 @@ import {
43
43
  WorkflowName,
44
44
  } from '../types';
45
45
  @Injectable()
46
- export class SimpleAppService<T extends { _id?: string }> {
46
+ export class SimpleAppService<T extends { _id?: string; __v?: number }> {
47
47
  @Inject(EventEmitter2)
48
48
  protected eventEmitter: EventEmitter2;
49
49
  @Inject(CloudApiService)
@@ -53,7 +53,12 @@ export class SimpleAppService<T extends { _id?: string }> {
53
53
  protected hooks: DefaultHooks<T> = {};
54
54
  protected logger = new Logger();
55
55
  protected strictIsolation = true;
56
- protected jsonschema = { type: 'object', properties: {}, required: [] };
56
+ protected jsonschema: any = {
57
+ type: 'object',
58
+ 'x-simpleapp-config': {},
59
+ properties: {},
60
+ required: [],
61
+ };
57
62
  protected documentIdentityCode = 'code';
58
63
  protected documentIdentityLabel = 'label';
59
64
  protected documentName = '-unknowndocname-';
@@ -97,6 +102,7 @@ export class SimpleAppService<T extends { _id?: string }> {
97
102
  getRecordId = (): string => this.data._id;
98
103
  setSchema = (newschema) => (this.jsonschema = newschema);
99
104
  getSchema = () => this.doc.schema.obj;
105
+ getJsonSchema = () => this.jsonschema;
100
106
  getHooks = () => this.hooks;
101
107
  getData = () => {
102
108
  //console.log('thisdata', this.data);
@@ -225,7 +231,9 @@ export class SimpleAppService<T extends { _id?: string }> {
225
231
 
226
232
  Object.assign(pipeline[0]['$match'], isolationFilter);
227
233
  //console.log("final agg",pipeline)
228
- return await this.doc.aggregate(pipeline);
234
+ return await this.doc.aggregate(pipeline, {
235
+ session: appuser.getDBSession(),
236
+ });
229
237
  } catch (err) {
230
238
  throw new InternalServerErrorException(err);
231
239
  }
@@ -238,8 +246,9 @@ export class SimpleAppService<T extends { _id?: string }> {
238
246
  async search(
239
247
  appuser: UserContext,
240
248
  filters: FilterQuery<T>,
241
- projection: ProjectionType<T> = undefined,
249
+ projection: string[] = undefined,
242
250
  sort: any = undefined,
251
+ lookup: { [key: string]: string } = undefined,
243
252
  ) {
244
253
  try {
245
254
  const isolationFilter = { ...this.getIsolationFilter(appuser) };
@@ -252,24 +261,47 @@ export class SimpleAppService<T extends { _id?: string }> {
252
261
  await this.hooks.beforeSearch(appuser, newfilters);
253
262
  // console.log("before _find",newfilters)
254
263
  // console.log("this.doc",this.doc)
255
- const products = await this.doc
256
- .find(newfilters, projection, { session: appuser.getDBSession() })
257
- .sort(sort);
258
- // console.log("after search",products)
259
- const productlist = products.map((p: T) => {
264
+ let searchResults: T[] = [];
265
+ if (lookup === undefined) {
266
+ this.logger.debug('after search', newfilters);
267
+ searchResults = await this.doc
268
+ .find(newfilters, projection, { session: appuser.getDBSession() })
269
+ .sort(sort);
270
+ } else {
271
+ const pipelines = this.searchToAggregate(
272
+ filters,
273
+ projection,
274
+ sort,
275
+ lookup,
276
+ );
277
+ this.logger.debug('after aggregate', pipelines);
278
+
279
+ searchResults = await this.aggregate(appuser, pipelines);
280
+ }
281
+
282
+ const list: T[] = searchResults.map((p: T) => {
260
283
  return p;
261
284
  });
262
285
  // console.log("after map",productlist)
263
- if (this.hooks.afterSearch)
264
- await this.hooks.afterSearch(appuser, productlist);
286
+ if (this.hooks.afterSearch) await this.hooks.afterSearch(appuser, list);
265
287
 
266
288
  // console.log(products);
267
- return productlist;
289
+ return list;
268
290
  } catch (err) {
269
291
  throw new BadRequestException(err.message);
270
292
  }
271
293
  // return this;
272
294
  }
295
+
296
+ async fullTextSearch(appuser: UserContext, keyword: string) {
297
+ const isolationFilter = { ...this.getIsolationFilter(appuser) };
298
+ this.polishIsolationFilter(isolationFilter);
299
+
300
+ const filters = { $text: { $search: keyword } };
301
+ const newfilters: FilterQuery<T> = { ...filters, ...isolationFilter };
302
+
303
+ return await this.doc.find(newfilters);
304
+ }
273
305
  async findById(appuser: UserContext, id: string) {
274
306
  if (this.hooks.beforeFetchRecord)
275
307
  await this.hooks.beforeFetchRecord(appuser, id);
@@ -364,6 +396,7 @@ export class SimpleAppService<T extends { _id?: string }> {
364
396
  // result = await newdoc.save()
365
397
  try {
366
398
  if (this.hooks.afterCreate) await this.hooks.afterCreate(appuser, result);
399
+ this.callWebhook(appuser, 'create', result);
367
400
  return result as T;
368
401
  } catch (err) {
369
402
  throw new InternalServerErrorException(
@@ -416,6 +449,7 @@ export class SimpleAppService<T extends { _id?: string }> {
416
449
  ajv.addFormat('text', /.*$/);
417
450
  ajv.addFormat('html', /.*$/);
418
451
  ajv.addFormat('documentno', /.*$/);
452
+ ajv.addFormat('money', /.*$/);
419
453
 
420
454
  ajv.addKeyword({ keyword: 'x-foreignkey', schemaType: 'string' });
421
455
  ajv.addKeyword({ keyword: 'x-simpleapp-config', schemaType: 'object' });
@@ -524,6 +558,7 @@ export class SimpleAppService<T extends { _id?: string }> {
524
558
  if (this.hooks.afterDelete)
525
559
  await this.hooks.afterDelete(appuser, deleteresult, id);
526
560
  //this.doc.findByIdAndDelete(id);
561
+ this.callWebhook(appuser, 'delete', deletedata);
527
562
  return deleteresult;
528
563
  } else {
529
564
  this.logger.debug('reject query', dependency);
@@ -542,6 +577,15 @@ export class SimpleAppService<T extends { _id?: string }> {
542
577
 
543
578
  findIdThenUpdate = async (appuser: UserContext, id: string, data: T) => {
544
579
  const existingdata = await this.findById(appuser, id);
580
+
581
+ //version exists, need ensure different only 1
582
+ if (typeof data.__v == 'number' && data.__v != existingdata.__v) {
583
+ throw new BadRequestException(
584
+ `You submit older version data "v${data.__v}"" but latest version = "v${existingdata.__v}"`,
585
+ );
586
+ }
587
+
588
+ data.__v = existingdata.__v + 1;
545
589
  if (!existingdata) {
546
590
  throw new NotFoundException(`${id} not found`, 'not found');
547
591
  }
@@ -575,6 +619,7 @@ export class SimpleAppService<T extends { _id?: string }> {
575
619
  appuser.addUpdatedRecordId(this.documentName, data._id);
576
620
  if (this.hooks.afterUpdate)
577
621
  await this.hooks.afterUpdate(appuser, id, existingdata, result);
622
+ this.callWebhook(appuser, 'update', result);
578
623
  return result; // await this.findById(appuser, id);
579
624
  } catch (err) {
580
625
  this.logger.error(err);
@@ -592,6 +637,13 @@ export class SimpleAppService<T extends { _id?: string }> {
592
637
  if (!existingdata) {
593
638
  throw new NotFoundException(`${id} not found`, 'not found');
594
639
  }
640
+ if (typeof data.__v == 'number' && data.__v != existingdata.__v) {
641
+ throw new BadRequestException(
642
+ `You submit older version data "v${data.__v}"" but latest version = "v${existingdata.__v}"`,
643
+ );
644
+ }
645
+
646
+ data.__v = existingdata.__v + 1;
595
647
 
596
648
  if (this.hooks.beforeUpdate)
597
649
  await this.hooks.beforeUpdate(appuser, id, data, existingdata);
@@ -628,6 +680,7 @@ export class SimpleAppService<T extends { _id?: string }> {
628
680
 
629
681
  if (this.hooks.afterUpdate)
630
682
  await this.hooks.afterUpdate(appuser, id, existingdata, result);
683
+ this.callWebhook(appuser, 'update', result);
631
684
  return result; //await this.findById(appuser, id);
632
685
  } catch (err) {
633
686
  throw new InternalServerErrorException(err.message);
@@ -731,6 +784,7 @@ export class SimpleAppService<T extends { _id?: string }> {
731
784
  if (this.hooks.afterSetStatus)
732
785
  await this.hooks.afterSetStatus(appuser, docstatus, finaldata);
733
786
 
787
+ this.callWebhook(appuser, docstatus, finaldata);
734
788
  return updateresult;
735
789
  }
736
790
  }
@@ -744,7 +798,7 @@ export class SimpleAppService<T extends { _id?: string }> {
744
798
  * @param {string} eventName The event name
745
799
  * @param {any} data The data
746
800
  */
747
- runBackgroundWorker(appuser: UserContext, eventName: string, payloads:any) {
801
+ runBackgroundWorker(appuser: UserContext, eventName: string, payloads: any) {
748
802
  this.eventEmitter.emit(eventName, appuser, payloads);
749
803
  }
750
804
 
@@ -764,7 +818,7 @@ export class SimpleAppService<T extends { _id?: string }> {
764
818
  * @param {any} data The data
765
819
  * @return {Promise} { description_of_the_return_value }
766
820
  */
767
- async runWorker(appuser: UserContext, eventName: string, payloads:any) {
821
+ async runWorker(appuser: UserContext, eventName: string, payloads: any) {
768
822
  const res = await this.eventEmitter.emitAsync(eventName, appuser, payloads);
769
823
  if (!this.eventEmitter.hasListeners(eventName)) {
770
824
  throw new InternalServerErrorException(`${eventName} seems no listener`);
@@ -915,4 +969,64 @@ export class SimpleAppService<T extends { _id?: string }> {
915
969
  const pdfresult = await this.printapi.getBase64Pdf(appuser, formatid, id);
916
970
  return pdfresult;
917
971
  }
972
+
973
+ searchToAggregate(
974
+ filter: FilterQuery<T>,
975
+ columns: string[],
976
+ sort: string[][],
977
+ lookup: { [key: string]: string },
978
+ ) {
979
+ const pipelines: PipelineStage[] = [];
980
+ const projection = {};
981
+ // console.log('sortsort', sort);
982
+
983
+ pipelines.push({ $match: filter });
984
+ if (Array.isArray(columns) && columns.length > 0) {
985
+ columns.forEach((col) => {
986
+ projection[col] = 1;
987
+ });
988
+ }
989
+
990
+ const collections = Object.keys(lookup);
991
+ collections.forEach((tokey: string) => {
992
+ const toarr = tokey.split('.');
993
+ const to = toarr[0];
994
+ const foreignField = toarr[1] ?? '_id';
995
+ pipelines.push({
996
+ $lookup: {
997
+ from: to,
998
+ as: '_' + to,
999
+ localField: lookup[tokey],
1000
+ foreignField: foreignField,
1001
+ },
1002
+ });
1003
+ pipelines.push({ $unwind: '$_' + to });
1004
+
1005
+ if (Object.keys(projection).length > 0) projection['_' + to] = 1;
1006
+ });
1007
+
1008
+ if (Object.keys(projection).length > 0)
1009
+ pipelines.push({ $project: projection });
1010
+
1011
+ if (Array.isArray(sort) && sort.length > 0) {
1012
+ const sortobj = {};
1013
+ sort.forEach((item) => {
1014
+ sortobj[item[0]] = item[1].toLowerCase() == 'asc' ? 1 : -1;
1015
+ });
1016
+ pipelines.push({ $sort: sortobj });
1017
+ }
1018
+ // console.log('pipelinespipelinespipelines', pipelines);
1019
+
1020
+ return pipelines;
1021
+ }
1022
+
1023
+ callWebhook(appuser: UserContext, actionName: string, data: any) {
1024
+ this.eventEmitter.emit(
1025
+ 'webhook',
1026
+ appuser,
1027
+ this.documentName,
1028
+ actionName,
1029
+ data,
1030
+ );
1031
+ }
918
1032
  }
@@ -30,17 +30,19 @@ export type DocumentStatus = {
30
30
  formula:string //example "jslib.getDocumentSubTotal(@F{$.details})"
31
31
  }
32
32
  export type SchemaConfig = {
33
- isolationType: IsolationType
33
+ isolationType: string
34
34
  requiredRoles?:string[]
35
35
  pageType?: string
36
36
  uniqueKey?:string
37
37
  uniqueKeys?:string[][]
38
38
  documentTitle?:string
39
39
  generateDocumentNumber?:boolean
40
+ docNoPattern?:string
40
41
  documentDate?:string
41
42
  allStatus?:DocumentStatus[]
42
43
  additionalApis?:DocumentApi[]
43
44
  additionalAutoCompleteFields ?: string[]
45
+ search?:string[]
44
46
  // libs?:ImportLibs[] // both process class and frontend client class will import same lib
45
47
  formulas?: Formula[]
46
48
  documentType: string
@@ -27,6 +27,7 @@ export type SearchBody = {
27
27
  fields?: any[];
28
28
 
29
29
  sorts?: any[];
30
+ lookup?:Object;
30
31
  };
31
32
 
32
33
  export enum IsolationType {
@@ -160,4 +160,23 @@ export class ProfileController {
160
160
  throw new HttpException('Forbidden', HttpStatus.FORBIDDEN);
161
161
  }
162
162
  }
163
+ @Post('/tour-complete/:guidename')
164
+ @Roles(Role.User)
165
+ @ApiOperation({
166
+ operationId: 'runTourComplete',
167
+ description: 'complete specific tour guide',
168
+ })
169
+ @ApiResponse({ status: 201, type: Object, description: 'Success' })
170
+ async runTourComplete(
171
+ @AppUser() appuser: UserContext,
172
+ @Param('guidename') guidename: string,
173
+
174
+ ) {
175
+ const result = await this.profileservice.runTourComplete(appuser,guidename,);
176
+ if (result) {
177
+ return result;
178
+ } else {
179
+ throw new HttpException('Forbidden', HttpStatus.FORBIDDEN);
180
+ }
181
+ }
163
182
  }