@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.
- package/README.md +6 -5
- package/dist/buildinschemas/branch.d.ts.map +1 -1
- package/dist/buildinschemas/branch.js +1 -2
- package/dist/buildinschemas/branch.js.map +1 -1
- package/dist/buildinschemas/changehistories.d.ts +3 -0
- package/dist/buildinschemas/changehistories.d.ts.map +1 -0
- package/dist/buildinschemas/changehistories.js +36 -0
- package/dist/buildinschemas/changehistories.js.map +1 -0
- package/dist/buildinschemas/index.d.ts +1 -0
- package/dist/buildinschemas/index.d.ts.map +1 -1
- package/dist/buildinschemas/index.js +3 -1
- package/dist/buildinschemas/index.js.map +1 -1
- package/dist/buildinschemas/organization.js +2 -2
- package/dist/buildinschemas/organization.js.map +1 -1
- package/dist/buildinschemas/user.d.ts.map +1 -1
- package/dist/buildinschemas/user.js +5 -1
- package/dist/buildinschemas/user.js.map +1 -1
- package/dist/buildinschemas/webhook.d.ts +3 -0
- package/dist/buildinschemas/webhook.d.ts.map +1 -0
- package/dist/buildinschemas/webhook.js +33 -0
- package/dist/buildinschemas/webhook.js.map +1 -0
- package/dist/framework.d.ts.map +1 -1
- package/dist/framework.js +3 -2
- package/dist/framework.js.map +1 -1
- package/dist/generate.js +30 -11
- package/dist/generate.js.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/processors/jsonschemabuilder.d.ts.map +1 -1
- package/dist/processors/jsonschemabuilder.js +10 -2
- package/dist/processors/jsonschemabuilder.js.map +1 -1
- package/dist/type.d.ts +2 -0
- package/dist/type.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/buildinschemas/branch.ts +1 -2
- package/src/buildinschemas/changehistories.ts +33 -0
- package/src/buildinschemas/index.ts +2 -1
- package/src/buildinschemas/organization.ts +2 -2
- package/src/buildinschemas/user.ts +5 -1
- package/src/buildinschemas/webhook.ts +31 -0
- package/src/framework.ts +3 -2
- package/src/generate.ts +35 -15
- package/src/index.ts +8 -3
- package/src/processors/jsonschemabuilder.ts +10 -2
- package/src/type.ts +2 -0
- package/templates/basic/nest/controller.ts.eta +23 -2
- package/templates/basic/nest/model.ts.eta +9 -1
- package/templates/basic/nest/resolver.ts.eta +2 -2
- package/templates/basic/nuxt/pages.[id].vue.eta +7 -7
- package/templates/basic/nuxt/pages.form.vue.eta +3 -6
- package/templates/basic/nuxt/pages.landing.vue.eta +2 -21
- package/templates/basic/nuxt/simpleapp.generate.client.ts.eta +8 -1
- package/templates/nest/src/simpleapp/generate/apischemas/simpleapp.apischema.ts.eta +2 -0
- package/templates/nest/src/simpleapp/generate/commons/dicts/documents.ts.eta +9 -2
- package/templates/nest/src/simpleapp/generate/commons/docnogenerator.service.ts.eta +36 -30
- package/templates/nest/src/simpleapp/generate/commons/roles/roles.enum.ts.eta +5 -10
- package/templates/nest/src/simpleapp/generate/commons/roles/roles.group.ts.eta +1 -0
- package/templates/nest/src/simpleapp/generate/commons/runwebhook.service.ts.eta +50 -0
- package/templates/nest/src/simpleapp/generate/commons/user.context.ts.eta +13 -3
- package/templates/nest/src/simpleapp/generate/controllers/simpleapp.controller.ts.eta +9 -1
- package/templates/nest/src/simpleapp/generate/processors/branch.processor.ts.eta +12 -6
- package/templates/nest/src/simpleapp/generate/processors/simpleapp.processor.ts.eta +128 -14
- package/templates/nest/src/simpleapp/generate/types/schema.type.ts.eta +3 -1
- package/templates/nest/src/simpleapp/generate/types/simpleapp.type.ts.eta +1 -0
- package/templates/nest/src/simpleapp/profile/profile.controller.ts.eta +19 -0
- package/templates/nest/src/simpleapp/profile/profile.service.ts.eta +30 -8
- package/templates/nest/src/simpleapp/simpleapp.module.ts.eta +2 -1
- package/templates/nuxt/{app.vue._eta → app.vue.eta} +3 -1
- package/templates/nuxt/assets/css/calendar.css._eta +3 -0
- package/templates/nuxt/assets/css/style.css._eta +1 -1
- package/templates/nuxt/assets/images/unknown.png.eta +0 -0
- package/templates/nuxt/assets/primevue/passthrough.ts._eta +6 -1
- package/templates/nuxt/components/button/ButtonAction.vue._eta +49 -7
- package/templates/nuxt/components/button/ButtonDanger.vue._eta +11 -3
- package/templates/nuxt/components/button/ButtonDefault.vue._eta +11 -3
- package/templates/nuxt/components/button/ButtonPrimary.vue._eta +9 -3
- package/templates/nuxt/components/button/ButtonSecondary.vue._eta +33 -0
- package/templates/nuxt/components/button/ButtonText.vue._eta +9 -5
- package/templates/nuxt/components/button/ButtonWarning.vue._eta +11 -3
- package/templates/nuxt/components/calendar/CalendarInput.vue.eta +17 -14
- package/templates/nuxt/components/calendar/CalendarSmall.vue.eta +33 -16
- package/templates/nuxt/components/chart/card.vue._eta +1 -1
- package/templates/nuxt/components/debug/DebugDocumentData.vue.eta +36 -26
- package/templates/nuxt/components/event/EventDocumentViewer.vue._eta +62 -22
- package/templates/nuxt/components/form/FormBranch.vue._eta +52 -5
- package/templates/nuxt/components/form/FormDocnoformat.vue.eta +14 -10
- package/templates/nuxt/components/form/FormUser.vue._eta +1 -1
- package/templates/nuxt/components/form/user/FormUserPermission.vue.eta +77 -59
- package/templates/nuxt/components/header/HeaderSelectBranch.vue.eta +42 -35
- package/templates/nuxt/components/image/ImageAvatar.vue.eta._vue +30 -0
- package/templates/nuxt/components/image/ImageOrganization.vue.eta.vue +7 -5
- package/templates/nuxt/components/image/ImageToBase64Uploader.vue.eta.vue +67 -50
- package/templates/nuxt/components/list/ListDocument.vue.eta +10 -5
- package/templates/nuxt/components/list/ListDocumentTable.vue.eta +21 -13
- package/templates/nuxt/components/list/ListMessages.vue.eta +1 -1
- package/templates/nuxt/components/list/ListView.vue.eta +94 -56
- package/templates/nuxt/components/overlay/OverlayPanelWithToolBar.vue.eta +11 -4
- package/templates/nuxt/components/overlay/OverlaySideBarCrud.vue.eta +17 -6
- package/templates/nuxt/components/overlay/OverlayViewer.vue.eta +16 -6
- package/templates/nuxt/components/page/PageDocList.vue.eta +108 -31
- package/templates/nuxt/components/renderer/RendererDate.vue.eta +8 -2
- package/templates/nuxt/components/renderer/RendererDateTime.vue.eta +7 -1
- package/templates/nuxt/components/renderer/RendererDocHistories.vue.eta +56 -0
- package/templates/nuxt/components/renderer/RendererForeignKey.vue.eta +9 -8
- package/templates/nuxt/components/renderer/RendererLink.vue.eta +7 -4
- package/templates/nuxt/components/renderer/RendererMoney.vue.eta +25 -17
- package/templates/nuxt/components/renderer/RendererTime.vue.eta +7 -1
- package/templates/nuxt/components/renderer/RendererViewer.vue.eta +19 -9
- package/templates/nuxt/components/select/SelectTemplate.vue.eta +76 -38
- package/templates/nuxt/components/session/SessionBlock.vue.eta +44 -46
- package/templates/nuxt/components/simpleApp/SimpleAppAutocomplete.vue.eta +61 -24
- package/templates/nuxt/components/simpleApp/SimpleAppCalendarInput.vue.eta +64 -0
- package/templates/nuxt/components/simpleApp/SimpleAppChildrenList.vue.eta +26 -14
- package/templates/nuxt/components/simpleApp/SimpleAppDocumentNo.vue.eta +8 -8
- package/templates/nuxt/components/simpleApp/SimpleAppFieldContainer.vue.eta +1 -1
- package/templates/nuxt/components/simpleApp/SimpleAppFormToolBar.vue._eta +128 -33
- package/templates/nuxt/components/simpleApp/SimpleAppInput.vue.eta +89 -168
- package/templates/nuxt/components/simpleApp/SimpleAppInputTable.vue.eta +43 -40
- package/templates/nuxt/components/simpleApp/SimpleAppUserPicker.vue.eta +387 -0
- package/templates/nuxt/components/text/TextDocStatus.vue._eta +22 -0
- package/templates/nuxt/components/user/UserButtonCreateTenant.vue._eta +13 -15
- package/templates/nuxt/components/user/UserButtonPermissionInfo.vue.eta +127 -93
- package/templates/nuxt/components/user/UserTenantPicker.vue.eta +1 -1
- package/templates/nuxt/composables/confirm.generate.ts.eta +19 -0
- package/templates/nuxt/composables/date.generate.ts.eta +106 -8
- package/templates/nuxt/composables/getDocument.generate.ts.eta +8 -6
- package/templates/nuxt/composables/getOpenApi.generate.ts.eta +58 -10
- package/templates/nuxt/composables/getUserStore.generate.ts.eta +37 -5
- package/templates/nuxt/composables/goTo.generate.ts.eta +14 -1
- package/templates/nuxt/composables/graphquery.generate.ts.eta +20 -2
- package/templates/nuxt/composables/recently.generate.ts.eta +16 -0
- package/templates/nuxt/composables/roles.generate.ts.eta +8 -13
- package/templates/nuxt/composables/stringHelper.generate.ts.eta +54 -1
- package/templates/nuxt/composables/sysmessage.generate.ts.eta +1 -1
- package/templates/nuxt/pages/[xorg]/docnoformat.vue.eta +1 -1
- package/templates/nuxt/pages/[xorg]/mobile/docnoformat/{index.vue.eta → index.vue.etaxxx} +1 -1
- package/templates/nuxt/pages/[xorg]/mobile/user/{index.vue.eta → index.vue.etaxxx} +1 -1
- package/templates/nuxt/pages/[xorg]/{organization.vue.eta → organization.vue._eta} +39 -10
- package/templates/nuxt/pages/[xorg]/profile.vue.eta +1 -1
- package/templates/nuxt/pages/[xorg]/user.vue.eta +13 -10
- package/templates/nuxt/pages/login.vue._eta +4 -1
- package/templates/nuxt/plugins/10.simpleapp-event.ts.eta +4 -0
- package/templates/nuxt/plugins/20.simpleapp-userstore.ts.eta +45 -26
- package/templates/nuxt/plugins/70.recently.ts.eta +55 -0
- package/templates/nuxt/providers/my-provider.ts.eta +22 -0
- package/templates/nuxt/server/api/[xorg]/{[...].ts.eta → [...].ts._eta} +47 -21
- package/templates/nuxt/simpleapp/generate/clients/SimpleAppClient.ts.eta +44 -3
- package/templates/nuxt/types/events.ts.eta +3 -2
- package/templates/nuxt/types/others.ts.eta +11 -1
- package/templates/nuxt/types/schema.ts.eta +3 -1
- package/templates/nuxt/types/simpleappinput.ts.eta +1 -1
- package/templates/nuxt/types/user.ts.eta +8 -7
- package/templates/project/jsonschemas/branch.json._eta +1 -0
- package/templates/project/jsonschemas/invoice.json._eta +4 -3
- package/templates/project/jsonschemas/organization.json._eta +2 -2
- package/templates/project/lang/default._json +5 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/templates/nuxt/components/image/ImageAvatar.vue.eta.vue +0 -38
- /package/templates/nuxt/pages/[xorg]/mobile/{index.vue._eta → index.vue._etaxxx} +0 -0
- /package/templates/nuxt/pages/[xorg]/mobile/organization/{[id].vue._eta → [id].vue._etaxxx} +0 -0
- /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 {
|
|
13
|
-
import moment from 'moment';
|
|
14
|
-
import
|
|
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
|
|
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
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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 =
|
|
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].
|
|
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 {
|
|
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) =>
|
|
37
|
-
|
|
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 =
|
|
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
|
|
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:
|
|
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
|
-
|
|
256
|
-
|
|
257
|
-
.
|
|
258
|
-
|
|
259
|
-
|
|
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
|
|
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:
|
|
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
|
|
@@ -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
|
}
|