From dd78ff5928bd977d79b9a40d934ddff761f086ff Mon Sep 17 00:00:00 2001 From: ezrren Date: Thu, 15 Jan 2026 17:57:55 +0800 Subject: [PATCH 1/5] add permission, profile update --- ReleaseNote.md | 6 ++++ package.json | 2 +- .../profile/profile.controller.ts.eta | 3 +- .../features/profile/profile.service.ts.eta | 23 ++++++++----- .../features/user-context/user.context.ts.eta | 34 +++++++++++++++++++ 5 files changed, 57 insertions(+), 11 deletions(-) diff --git a/ReleaseNote.md b/ReleaseNote.md index b7700eb..faf526b 100644 --- a/ReleaseNote.md +++ b/ReleaseNote.md @@ -1,3 +1,9 @@ +[2.0.2k-alpha] + +1. add permission devbilling can access tenant invoice page +2. add permission devsupport can access tenant page +3. update profile tenant for user friendly when create tenant + [2.0.2i-alpha] 1. fix bugs cannot run backend diff --git a/package.json b/package.json index eb0825f..212cdf5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@simitgroup/simpleapp-generator", - "version": "2.0.2i-alpha", + "version": "2.0.2k-alpha", "description": "frontend nuxtjs and backend nests code generator using jsonschema", "main": "dist/index.js", "scripts": { diff --git a/templates/nest/src/simple-app/_core/features/profile/profile.controller.ts.eta b/templates/nest/src/simple-app/_core/features/profile/profile.controller.ts.eta index bf21c77..b7a597c 100644 --- a/templates/nest/src/simple-app/_core/features/profile/profile.controller.ts.eta +++ b/templates/nest/src/simple-app/_core/features/profile/profile.controller.ts.eta @@ -116,8 +116,9 @@ export class ProfileController { @Body('timeZone') timeZone: string, @Body('utcOffset') utcOffset: number, @Body('businessType') businessType: string, + @Body('interestedInSales') interestedInSales?: boolean, ) { - const result = await this.profileservice.createTenant(appUser, tenantName, timeZone, utcOffset, businessType,mobileNo); + const result = await this.profileservice.createTenant(appUser, tenantName, timeZone, utcOffset, businessType, mobileNo, interestedInSales); if (result) { return result; } else { diff --git a/templates/nest/src/simple-app/_core/features/profile/profile.service.ts.eta b/templates/nest/src/simple-app/_core/features/profile/profile.service.ts.eta index 732a9b7..8d8802c 100644 --- a/templates/nest/src/simple-app/_core/features/profile/profile.service.ts.eta +++ b/templates/nest/src/simple-app/_core/features/profile/profile.service.ts.eta @@ -87,7 +87,7 @@ export class ProfileService { return userinfo; } - async createTenant(appuser: UserContext, tenantName: string, timeZone: string, utcOffset: number, businessType: string, mobileNo: string) { + async createTenant(appuser: UserContext, tenantName: string, timeZone: string, utcOffset: number, businessType: string, mobileNo: string, interestedInSales?: boolean) { // try{ @@ -111,6 +111,8 @@ export class ProfileService { _id: appuser.getId(), label: appuser.getFullname(), uid: appuser.getUid(), + mobileNo: mobileNo, + email: appuser.getEmail(), }, }; this.logger.debug(tenantdata, 'createTenant data'); @@ -149,21 +151,21 @@ export class ProfileService { branchName: tenantName, companyName: tenantName, registrationNo: '', - paymentRemark: '', - invoiceRemark: '', - cnRemark: '', - refundRemark: '', + paymentRemark: 'This document is system-generated and does not require a signature. For any questions, please contact our support team.', + invoiceRemark: 'This document is system-generated and does not require a signature. For any questions, please contact our support team.', + cnRemark: 'This document is system-generated and does not require a signature. For any questions, please contact our support team.', + refundRemark: 'This document is system-generated and does not require a signature. For any questions, please contact our support team.', workStart: '09:00:00', workEnd: '22:00:00', workingHours: [], offdays: ['sat', 'sun'], - street1: '11,my street 1', - street2: 'my street 2', + street1: '11, My Street 1', + street2: 'My Street 2', postcode: '11111', - city: 'my city', + city: 'My City', tel: mobileNo, email: appuser.getEmail(), - region: 'my region', + region: 'My Region', country: countryName, active: true, orgId: orgResult.orgId, @@ -187,6 +189,8 @@ export class ProfileService { fullName: appuser.getFullname(), email: appuser.getEmail(), active: true, + mobileNo: mobileNo, + interestedInSales: interestedInSales, }; this.logger.debug(userdata, 'createtenant user data'); const userResult = await this.userService.create(appuser, userdata); @@ -219,6 +223,7 @@ export class ProfileService { const tenantUpdateData = await this.tenantService.findById(appuser, tenantResult._id); this.logger.log(tenantUpdateData, `update tenant owner(${tenantResult._id})`); tenantUpdateData.owner._id = userRecordId; + (tenantUpdateData.owner as any).mobileNo = mobileNo; const updateTenantOwnerResult = await this.tenantService.findIdThenUpdate(appuser, tenantResult._id, tenantUpdateData); if (!updateTenantOwnerResult) { throw new BadRequestException('Update tenant owner failed'); diff --git a/templates/nest/src/simple-app/_core/features/user-context/user.context.ts.eta b/templates/nest/src/simple-app/_core/features/user-context/user.context.ts.eta index 1560eb4..03173fc 100644 --- a/templates/nest/src/simple-app/_core/features/user-context/user.context.ts.eta +++ b/templates/nest/src/simple-app/_core/features/user-context/user.context.ts.eta @@ -1123,6 +1123,40 @@ export class UserContext extends UserContextInfo { } } + if (this.roles.includes(Role.DevSupport)) { + const tenantRoles = [ + Role.Tenant_access, + Role.Tenant_search, + Role.Tenant_create, + Role.Tenant_update, + Role.Tenant_delete, + ]; + for (let r = 0; r < tenantRoles.length; r++) { + if (!this.roles.includes(tenantRoles[r])) { + this.roles.push(tenantRoles[r]); + } + } + } + + if (this.roles.includes(Role.DevBilling)) { + const tenantInvoiceRoles = [ + Role.TenantInvoice_access, + Role.TenantInvoice_search, + Role.TenantInvoice_create, + Role.TenantInvoice_update, + Role.TenantInvoice_delete, + Role.TenantInvoice_draft, + Role.TenantInvoice_void, + Role.TenantInvoice_confirm, + Role.TenantInvoice_print, + ]; + for (let r = 0; r < tenantInvoiceRoles.length; r++) { + if (!this.roles.includes(tenantInvoiceRoles[r])) { + this.roles.push(tenantInvoiceRoles[r]); + } + } + } + this.moreProps = this.setMoreProps(userProfile); } else { this.logger.debug(`User ${this.uid} not exists in tenant (${this.tenantId})`); From 65d290c52b983b9b196fb6d9c7b89a3a60387554 Mon Sep 17 00:00:00 2001 From: ezrren Date: Fri, 16 Jan 2026 11:28:24 +0800 Subject: [PATCH 2/5] user context --- .../features/user-context/user.context.ts.eta | 140 +++++++++--------- 1 file changed, 66 insertions(+), 74 deletions(-) diff --git a/templates/nest/src/simple-app/_core/features/user-context/user.context.ts.eta b/templates/nest/src/simple-app/_core/features/user-context/user.context.ts.eta index 03173fc..7aca24c 100644 --- a/templates/nest/src/simple-app/_core/features/user-context/user.context.ts.eta +++ b/templates/nest/src/simple-app/_core/features/user-context/user.context.ts.eta @@ -37,8 +37,8 @@ import { StepData } from 'src/simple-app/_core/resources/permission/permission.s @Injectable({ scope: Scope.REQUEST }) export class UserContext extends UserContextInfo { sessionId: string = crypto.randomUUID(); - protected isTransaction = false - protected transSteps:StepData[] = [] + protected isTransaction = false; + protected transSteps: StepData[] = []; protected logger = new Logger(this.constructor.name); // protected uid: string = ''; @@ -152,7 +152,7 @@ export class UserContext extends UserContextInfo { getDBSession = (): ClientSession => this.dbsession; getTransSteps = () => this.transSteps; - setTransSteps = (steps: StepData[]) => this.transSteps = steps; + setTransSteps = (steps: StepData[]) => (this.transSteps = steps); getId = () => this._id; getUid = () => this.uid; @@ -578,6 +578,42 @@ export class UserContext extends UserContextInfo { } } + // Tenant permissions for DevSupport role + if (this.roles.includes(Role.DevSupport)) { + const tenantRoles = [ + Role.Tenant_access, + Role.Tenant_search, + Role.Tenant_create, + Role.Tenant_update, + Role.Tenant_delete, + ]; + for (let r = 0; r < tenantRoles.length; r++) { + if (!this.roles.includes(tenantRoles[r])) { + this.roles.push(tenantRoles[r]); + } + } + } + + // TenantInvoice permissions for DevBilling role + if (this.roles.includes(Role.DevBilling)) { + const tenantInvoiceRoles = [ + Role.TenantInvoice_access, + Role.TenantInvoice_search, + Role.TenantInvoice_create, + Role.TenantInvoice_update, + Role.TenantInvoice_delete, + Role.TenantInvoice_draft, + Role.TenantInvoice_void, + Role.TenantInvoice_confirm, + Role.TenantInvoice_print, + ]; + for (let r = 0; r < tenantInvoiceRoles.length; r++) { + if (!this.roles.includes(tenantInvoiceRoles[r])) { + this.roles.push(tenantInvoiceRoles[r]); + } + } + } + this.moreProps = this.setMoreProps(userProfile); // this.package = userProfile['package']; // this.appintegration = await this.setAppIntegration(); @@ -1123,40 +1159,6 @@ export class UserContext extends UserContextInfo { } } - if (this.roles.includes(Role.DevSupport)) { - const tenantRoles = [ - Role.Tenant_access, - Role.Tenant_search, - Role.Tenant_create, - Role.Tenant_update, - Role.Tenant_delete, - ]; - for (let r = 0; r < tenantRoles.length; r++) { - if (!this.roles.includes(tenantRoles[r])) { - this.roles.push(tenantRoles[r]); - } - } - } - - if (this.roles.includes(Role.DevBilling)) { - const tenantInvoiceRoles = [ - Role.TenantInvoice_access, - Role.TenantInvoice_search, - Role.TenantInvoice_create, - Role.TenantInvoice_update, - Role.TenantInvoice_delete, - Role.TenantInvoice_draft, - Role.TenantInvoice_void, - Role.TenantInvoice_confirm, - Role.TenantInvoice_print, - ]; - for (let r = 0; r < tenantInvoiceRoles.length; r++) { - if (!this.roles.includes(tenantInvoiceRoles[r])) { - this.roles.push(tenantInvoiceRoles[r]); - } - } - } - this.moreProps = this.setMoreProps(userProfile); } else { this.logger.debug(`User ${this.uid} not exists in tenant (${this.tenantId})`); @@ -1255,54 +1257,44 @@ export class UserContext extends UserContextInfo { return isodate; } + addTransactionStep(action: string, collection: string, id: string[], data: any[]) { + this.transSteps.push({ action: action, collection: collection, id: id, data: data }); + } - addTransactionStep (action:string,collection:string,id:string[], data:any[]) { - this.transSteps.push({action:action,collection:collection,id:id,data:data}) - } - - - inTransaction(){ - if(process.env.MONGO_TRANS==='false') - return this.isTransaction - else - return this.dbsession && this.dbsession.inTransaction() ? true : false + inTransaction() { + if (process.env.MONGO_TRANS === 'false') return this.isTransaction; + else return this.dbsession && this.dbsession.inTransaction() ? true : false; } - startTransaction(){ - if(process.env.MONGO_TRANS==='false'){ - this.isTransaction=true - this.transSteps=[] - - - }else{ - this.dbsession.startTransaction({ readConcern: { level: 'snapshot' }, writeConcern: { w: 'majority' }, readPreference: 'primary' }); + startTransaction() { + if (process.env.MONGO_TRANS === 'false') { + this.isTransaction = true; + this.transSteps = []; + } else { + this.dbsession.startTransaction({ readConcern: { level: 'snapshot' }, writeConcern: { w: 'majority' }, readPreference: 'primary' }); } } - async commitTransaction(){ - if(process.env.MONGO_TRANS==='false'){ - this.isTransaction=false - this.transSteps=[] - }else{ - if(this.dbsession.inTransaction()) - await this.dbsession.commitTransaction() + async commitTransaction() { + if (process.env.MONGO_TRANS === 'false') { + this.isTransaction = false; + this.transSteps = []; + } else { + if (this.dbsession.inTransaction()) await this.dbsession.commitTransaction(); } } - async rollBackTransaction(dbService:SimpleAppDbRevertService){ - if(process.env.MONGO_TRANS==='false'){ - await dbService.revertSteps(this.transSteps) - this.isTransaction=false - this.transSteps=[] - - }else{ - if(this.dbsession.inTransaction()) - await this.dbsession.abortTransaction() + async rollBackTransaction(dbService: SimpleAppDbRevertService) { + if (process.env.MONGO_TRANS === 'false') { + await dbService.revertSteps(this.transSteps); + this.isTransaction = false; + this.transSteps = []; + } else { + if (this.dbsession.inTransaction()) await this.dbsession.abortTransaction(); } } - async endSession(){ - await this.dbsession.endSession() + async endSession() { + await this.dbsession.endSession(); } } - // type StepData = {action:string,collection:string,id:string[],data:any[]} /** * Define a type for userinfo From d116109673e40f0454d6f3e7b98e23e595fc76a6 Mon Sep 17 00:00:00 2001 From: ezrren Date: Fri, 16 Jan 2026 11:29:33 +0800 Subject: [PATCH 3/5] package --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 212cdf5..7aef774 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@simitgroup/simpleapp-generator", - "version": "2.0.2k-alpha", + "version": "2.0.2-k-alpha", "description": "frontend nuxtjs and backend nests code generator using jsonschema", "main": "dist/index.js", "scripts": { @@ -39,7 +39,7 @@ "tslog": "4.9.1" }, "bin": { - "simpleapp-generator": "./dist/index.js" + "simpleapp-generator": "dist/index.js" }, "devDependencies": { "@types/node": "^20.5.2", From 94d743acf436f85ec6ca7db86ad1fcb92357f71a Mon Sep 17 00:00:00 2001 From: ezrren Date: Fri, 16 Jan 2026 11:41:05 +0800 Subject: [PATCH 4/5] package 2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7aef774..4e2ddbc 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@simitgroup/simpleapp-generator", "version": "2.0.2-k-alpha", - "description": "frontend nuxtjs and backend nests code generator using jsonschema", + "description": "frontend nuxtjs and backend nests code generator using jsonschema.", "main": "dist/index.js", "scripts": { "generate": "ts-node src/index.ts -c ./sampleconfig.json; pnpm exec prettier ./backend --write;pnpm exec prettier ./frontend --write", From 281ad4096caebe9da565429eabcbebb4a554c7f5 Mon Sep 17 00:00:00 2001 From: ezrren Date: Wed, 21 Jan 2026 11:10:49 +0800 Subject: [PATCH 5/5] frontend --- ReleaseNote.md | 7 +++++++ package.json | 2 +- templates/nuxt/app.vue.eta | 11 +++++++---- .../simpleApp/SimpleAppAutocomplete.vue.eta | 3 +++ .../nuxt/components/simpleApp/SimpleAppInput.vue.eta | 1 + templates/nuxt/composables/date.generate.ts.eta | 10 +++++----- templates/nuxt/plugins/20.simpleapp-userstore.ts.eta | 11 +++++++++++ 7 files changed, 35 insertions(+), 10 deletions(-) diff --git a/ReleaseNote.md b/ReleaseNote.md index faf526b..e0ae902 100644 --- a/ReleaseNote.md +++ b/ReleaseNote.md @@ -1,3 +1,10 @@ +[2.0.2l-alpha] + +1. change date to [dd/mm/yyyy] +2. add new role such isBillingUser, isInternalUser and isDevSupport to access certain page +3. adjust template for make the banner free and expired warning appear +4. Added new paras prop for passing additional parameters on input + [2.0.2k-alpha] 1. add permission devbilling can access tenant invoice page diff --git a/package.json b/package.json index 4e2ddbc..d333726 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@simitgroup/simpleapp-generator", - "version": "2.0.2-k-alpha", + "version": "2.0.2-l-alpha", "description": "frontend nuxtjs and backend nests code generator using jsonschema.", "main": "dist/index.js", "scripts": { diff --git a/templates/nuxt/app.vue.eta b/templates/nuxt/app.vue.eta index ebdb89c..e498d2a 100644 --- a/templates/nuxt/app.vue.eta +++ b/templates/nuxt/app.vue.eta @@ -9,6 +9,8 @@ + + @@ -20,6 +22,7 @@ * last change 2024-03-17 * Author: Ks Tan */ +import SubscriptionFreeBanner from "~/components/subscription/SubscriptionFreeBanner.vue"; let currentXorg = getCurrentXorg() watch(()=>useRoute().fullPath,async (newval,oldvalue)=>{ if(getCurrentXorg()!=currentXorg){ @@ -29,9 +32,9 @@ watch(()=>useRoute().fullPath,async (newval,oldvalue)=>{ if(getPathPara('xorg','')!=''){ if(getCurrentXorg()===''){ - navigateTo('/picktenant') + goTo('/picktenant') }else if(!getUserProfile()?.currentGroup){ - goTo('pickgroup') + navigateTo('pickgroup') } setGraphqlServer() } @@ -54,9 +57,9 @@ onMounted(async()=>{ const currentgroup = useCookie('currentGroup').value //if no xorg, no enforce pick group if(getCurrentXorg()===''){ - navigateTo('/picktenant') + goTo('/picktenant') }else if(!currentgroup){ - goTo('/pickgroup') + navigateTo('/pickgroup') } setGraphqlServer() }else{ diff --git a/templates/nuxt/components/simpleApp/SimpleAppAutocomplete.vue.eta b/templates/nuxt/components/simpleApp/SimpleAppAutocomplete.vue.eta index c80f88a..2ab10ec 100644 --- a/templates/nuxt/components/simpleApp/SimpleAppAutocomplete.vue.eta +++ b/templates/nuxt/components/simpleApp/SimpleAppAutocomplete.vue.eta @@ -160,6 +160,7 @@ getDocument(setting.fieldsetting['x-foreignkey']).viewer, ) " + :paras="props.paras" @after=" async (eventType: FormCrudEvent, data: any, result: any) => await afterRenderMobileForm(eventType, data) @@ -206,6 +207,7 @@ const props = withDefaults( componentProps?: AutoCompleteProps; autocompleteFilter?: any; pt?: any; + paras?: any; }>(), { allowAddNew: true, @@ -358,6 +360,7 @@ const openViewer = (readonly: boolean) => { readonly: readonly, viewer: getDocument(docname)?.viewer, documentName: docname, + paras: props.paras, //after create, auto copy value into auto complete after: async (eventType: FormCrudEvent, data: any) => { diff --git a/templates/nuxt/components/simpleApp/SimpleAppInput.vue.eta b/templates/nuxt/components/simpleApp/SimpleAppInput.vue.eta index 6a71ee0..38feb21 100644 --- a/templates/nuxt/components/simpleApp/SimpleAppInput.vue.eta +++ b/templates/nuxt/components/simpleApp/SimpleAppInput.vue.eta @@ -205,6 +205,7 @@ :readonly="isReadonly" :placeholder="placeholder" :autocomplete-filter="autocompleteFilter" + :paras="paras" @change="onChange" >