From 8d7b9f5d8b2434878265fdcff927bdf80d136945 Mon Sep 17 00:00:00 2001 From: praju-aot Date: Mon, 9 Sep 2024 16:18:36 -0400 Subject: [PATCH 1/3] ORV2-2682 - Staff Manage queue - send notification on approval and rejection --- dops/src/app.service.ts | 16 ++ .../templates/application-approved.email.hbs | 138 ++++++++++++++ .../templates/application-rejected.email.hbs | 179 ++++++++++++++++++ dops/src/enum/cache-key.enum.ts | 2 + dops/src/enum/notification-template.enum.ts | 2 + dops/src/helper/notification.helper.ts | 4 + .../common/enum/notification-template.enum.ts | 2 + ...ication-approved.notification.interface.ts | 5 + ...ication-rejected.notification.interface.ts | 4 + .../interface/notification.interface.ts | 8 +- .../application/application.service.ts | 64 +++++++ 11 files changed, 423 insertions(+), 1 deletion(-) create mode 100644 dops/src/assets/templates/application-approved.email.hbs create mode 100644 dops/src/assets/templates/application-rejected.email.hbs create mode 100644 vehicles/src/common/interface/application-approved.notification.interface.ts create mode 100644 vehicles/src/common/interface/application-rejected.notification.interface.ts diff --git a/dops/src/app.service.ts b/dops/src/app.service.ts index 2675df541..bd3e60b8e 100644 --- a/dops/src/app.service.ts +++ b/dops/src/app.service.ts @@ -109,6 +109,22 @@ export class AppService { ), ); + await addToCache( + this.cacheManager, + CacheKey.EMAIL_TEMPLATE_APPLICATION_APPROVED, + this.convertFileToString( + assetsPath + 'templates/application-approved.email.hbs', + ), + ); + + await addToCache( + this.cacheManager, + CacheKey.EMAIL_TEMPLATE_APPLICATION_REJECTED, + this.convertFileToString( + assetsPath + 'templates/application-rejected.email.hbs', + ), + ); + const featureFlags = await this.featureFlagsService.findAll(); await addToCache( this.cacheManager, diff --git a/dops/src/assets/templates/application-approved.email.hbs b/dops/src/assets/templates/application-approved.email.hbs new file mode 100644 index 000000000..95945e9da --- /dev/null +++ b/dops/src/assets/templates/application-approved.email.hbs @@ -0,0 +1,138 @@ + + + + + Application Approved + + +
+
+
+
+ motiBCLogo +
+ +
+ +
+
+

+ Application {{ applicationNumber }} for plate {{ plate }} has been + approved and is now ready for purchase in the {{ companyName }} shopping + cart. +

+
+ +
+

+ You can apply for additional permits online at + https://onroutebc.gov.bc.ca/. +

+
+ +
+

+ + THIS EMAIL BOX IS NOT MONITORED AND ANY EMAILS SENT TO IT WILL NOT BE + ANSWERED. IF YOU HAVE ANY QUESTIONS, REQUESTS, PLEASE EMAIL: + + ppcpermit@gov.bc.ca. If you have received this message in error, then please contact + ppcpermit@gov.bc.ca + and then immediately delete/discard this transmission, including all + attachments, without copying, distributing or disclosing same. +

+
+ +
+
+
+
+ onRouteBCLogo +
+ +
+ +

+ Have questions or need help? Check out the + Commercial Vehicle Permits + page or email us at + onRouteBc@gov.bc.ca. +

+ +
+ +

+ Terms of Service + and + Privacy Policy +

+
+
+
+
diff --git a/dops/src/assets/templates/application-rejected.email.hbs b/dops/src/assets/templates/application-rejected.email.hbs new file mode 100644 index 000000000..d6675b516 --- /dev/null +++ b/dops/src/assets/templates/application-rejected.email.hbs @@ -0,0 +1,179 @@ + + + + + Application Rejected + + +
+
+
+
+ motiBCLogo +
+ +
+ +
+
+

+ Reason for rejection: +

+
+

+ {{ rejectedDateTime }} +

+

+ {{ rejectedReason }} +

+
+
+ +
+

+ You can apply for additional permits online at + https://onroutebc.gov.bc.ca/. +

+
+ +
+

+ + THIS EMAIL BOX IS NOT MONITORED AND ANY EMAILS SENT TO IT WILL NOT BE + ANSWERED. IF YOU HAVE ANY QUESTIONS, REQUESTS, PLEASE EMAIL: + + ppcpermit@gov.bc.ca. If you have received this message in error, then please contact + ppcpermit@gov.bc.ca + and then immediately delete/discard this transmission, including all + attachments, without copying, distributing or disclosing same. +

+
+ +
+
+
+
+ onRouteBCLogo +
+ +
+ +

+ Have questions or need help? Check out the + Commercial Vehicle Permits + page or email us at + onRouteBc@gov.bc.ca. +

+ +
+ +

+ Terms of Service + and + Privacy Policy +

+
+
+
+
diff --git a/dops/src/enum/cache-key.enum.ts b/dops/src/enum/cache-key.enum.ts index ab7cf9901..7897b590e 100644 --- a/dops/src/enum/cache-key.enum.ts +++ b/dops/src/enum/cache-key.enum.ts @@ -10,6 +10,8 @@ export enum CacheKey { EMAIL_TEMPLATE_PAYMENT_RECEIPT = 'EMAIL_TEMPLATE_PAYMENT_RECEIPT', EMAIL_TEMPLATE_COMPANY_SUSPEND = 'EMAIL_TEMPLATE_COMPANY_SUSPEND', EMAIL_TEMPLATE_COMPANY_UNSUSPEND = 'EMAIL_TEMPLATE_COMPANY_UNSUSPEND', + EMAIL_TEMPLATE_APPLICATION_APPROVED = 'EMAIL_TEMPLATE_APPLICATION_APPROVED', + EMAIL_TEMPLATE_APPLICATION_REJECTED = 'EMAIL_TEMPLATE_APPLICATION_REJECTED', EMAIL_TEMPLATE_ORBC_STYLE = 'EMAIL_TEMPLATE_ORBC_STYLE', CHES_ACCESS_TOKEN = 'CHES_ACCESS_TOKEN', CDOGS_ACCESS_TOKEN = 'CDOGS_ACCESS_TOKEN', diff --git a/dops/src/enum/notification-template.enum.ts b/dops/src/enum/notification-template.enum.ts index 304f130a4..40439dfa9 100644 --- a/dops/src/enum/notification-template.enum.ts +++ b/dops/src/enum/notification-template.enum.ts @@ -4,4 +4,6 @@ export enum NotificationTemplate { PAYMENT_RECEIPT = 'PAYMENT_RECEIPT', COMPANY_SUSPEND = 'COMPANY_SUSPEND', COMPANY_UNSUSPEND = 'COMPANY_UNSUSPEND', + APPLICATION_APPROVED = 'APPLICATION_APPROVED', + APPLICATION_REJECTED = 'APPLICATION_REJECTED', } diff --git a/dops/src/helper/notification.helper.ts b/dops/src/helper/notification.helper.ts index 4ce435793..5be42710d 100644 --- a/dops/src/helper/notification.helper.ts +++ b/dops/src/helper/notification.helper.ts @@ -39,6 +39,10 @@ export const getCacheKeyforEmailTemplate = ( return CacheKey.EMAIL_TEMPLATE_COMPANY_SUSPEND; case NotificationTemplate.COMPANY_UNSUSPEND: return CacheKey.EMAIL_TEMPLATE_COMPANY_UNSUSPEND; + case NotificationTemplate.APPLICATION_APPROVED: + return CacheKey.EMAIL_TEMPLATE_APPLICATION_APPROVED; + case NotificationTemplate.APPLICATION_REJECTED: + return CacheKey.EMAIL_TEMPLATE_APPLICATION_REJECTED; default: throw new Error('Invalid template name'); } diff --git a/vehicles/src/common/enum/notification-template.enum.ts b/vehicles/src/common/enum/notification-template.enum.ts index 304f130a4..40439dfa9 100644 --- a/vehicles/src/common/enum/notification-template.enum.ts +++ b/vehicles/src/common/enum/notification-template.enum.ts @@ -4,4 +4,6 @@ export enum NotificationTemplate { PAYMENT_RECEIPT = 'PAYMENT_RECEIPT', COMPANY_SUSPEND = 'COMPANY_SUSPEND', COMPANY_UNSUSPEND = 'COMPANY_UNSUSPEND', + APPLICATION_APPROVED = 'APPLICATION_APPROVED', + APPLICATION_REJECTED = 'APPLICATION_REJECTED', } diff --git a/vehicles/src/common/interface/application-approved.notification.interface.ts b/vehicles/src/common/interface/application-approved.notification.interface.ts new file mode 100644 index 000000000..114692a1b --- /dev/null +++ b/vehicles/src/common/interface/application-approved.notification.interface.ts @@ -0,0 +1,5 @@ +export interface ApplicationApprovedNotification { + applicationNumber: string; + plate: string; + companyName: string; +} diff --git a/vehicles/src/common/interface/application-rejected.notification.interface.ts b/vehicles/src/common/interface/application-rejected.notification.interface.ts new file mode 100644 index 000000000..24d1fb3d9 --- /dev/null +++ b/vehicles/src/common/interface/application-rejected.notification.interface.ts @@ -0,0 +1,4 @@ +export interface ApplicationRejectedNotification { + rejectedDateTime: string; + rejectedReason: string; +} diff --git a/vehicles/src/common/interface/notification.interface.ts b/vehicles/src/common/interface/notification.interface.ts index 62f69c0ad..3af464c72 100644 --- a/vehicles/src/common/interface/notification.interface.ts +++ b/vehicles/src/common/interface/notification.interface.ts @@ -1,4 +1,6 @@ import { NotificationTemplate } from '../enum/notification-template.enum'; +import { ApplicationApprovedNotification } from './application-approved.notification.interface'; +import { ApplicationRejectedNotification } from './application-rejected.notification.interface'; import { CompanyDataNotification } from './company-data.notification.interface'; import { ProfileRegistrationDataNotification } from './profile-registration-data.notification.interface'; @@ -9,5 +11,9 @@ export interface INotification { bcc?: string[]; fax?: string[]; templateName: NotificationTemplate; - data?: CompanyDataNotification | ProfileRegistrationDataNotification; + data?: + | CompanyDataNotification + | ProfileRegistrationDataNotification + | ApplicationApprovedNotification + | ApplicationRejectedNotification; } diff --git a/vehicles/src/modules/permit-application-payment/application/application.service.ts b/vehicles/src/modules/permit-application-payment/application/application.service.ts index aeaa42f16..fab0a09e8 100644 --- a/vehicles/src/modules/permit-application-payment/application/application.service.ts +++ b/vehicles/src/modules/permit-application-payment/application/application.service.ts @@ -65,6 +65,13 @@ import { throwUnprocessableEntityException } from '../../../common/helper/except import { ApplicationSearch } from '../../../common/enum/application-search.enum'; import { CaseStatusType } from '../../../common/enum/case-status-type.enum'; +import { INotificationDocument } from '../../../common/interface/notification-document.interface'; +import { validateEmailandFaxList } from '../../../common/helper/notification.helper'; +import { NotificationTemplate } from '../../../common/enum/notification-template.enum'; +import { PermitData } from '../../../common/interface/permit.template.interface'; +import { ApplicationApprovedNotification } from '../../../common/interface/application-approved.notification.interface'; +import { ApplicationRejectedNotification } from '../../../common/interface/application-rejected.notification.interface'; +import { convertUtcToPt } from '../../../common/helper/date-time.helper'; @Injectable() export class ApplicationService { @@ -1012,6 +1019,63 @@ export class ApplicationService { }, ); await queryRunner.commitTransaction(); + try { + if ( + caseActivityType === CaseActivityType.APPROVED || + caseActivityType === CaseActivityType.REJECTED + ) { + let notificationTemplate: NotificationTemplate; + let subject: string; + let notificationData: + | ApplicationApprovedNotification + | ApplicationRejectedNotification; + + const permitData = JSON.parse( + application.permitData.permitData, + ) as PermitData; + + if (caseActivityType === CaseActivityType.APPROVED) { + notificationTemplate = NotificationTemplate.APPLICATION_APPROVED; + subject = `onRouteBC Permit Application ${application?.applicationNumber} Approved`; + notificationData = { + applicationNumber: application?.applicationNumber, + companyName: application?.company?.legalName, + plate: permitData?.vehicleDetails?.plate, + } as ApplicationApprovedNotification; + } else { + notificationTemplate = NotificationTemplate.APPLICATION_REJECTED; + subject = `onRouteBC Permit Application ${application?.applicationNumber} for Plate ${permitData?.vehicleDetails?.plate} Rejected`; + notificationData = { + rejectedDateTime: convertUtcToPt( + new Date(), + 'MMM. D, YYYY, hh:mm a Z', + ), + rejectedReason: comment, + } as ApplicationRejectedNotification; + } + + const emailList = [ + permitData?.contactDetails?.email, + permitData?.contactDetails?.additionalEmail, + application?.company?.email, + ]; + const notificationDocument: INotificationDocument = { + templateName: notificationTemplate, + to: validateEmailandFaxList(emailList), + subject: subject, + data: notificationData, + }; + + void this.dopsService.notificationWithDocumentsFromDops( + currentUser, + notificationDocument, + true, + ); + } + } catch (error) { + this.logger.error(error); //Swallow Notification error + } + return result; } catch (error) { await queryRunner.rollbackTransaction(); From 0a0655e3d48a06a50be982688641b984c621da85 Mon Sep 17 00:00:00 2001 From: praju-aot Date: Mon, 9 Sep 2024 16:22:26 -0400 Subject: [PATCH 2/3] Update the subject for approval email --- .../application/application.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vehicles/src/modules/permit-application-payment/application/application.service.ts b/vehicles/src/modules/permit-application-payment/application/application.service.ts index fab0a09e8..991dee10c 100644 --- a/vehicles/src/modules/permit-application-payment/application/application.service.ts +++ b/vehicles/src/modules/permit-application-payment/application/application.service.ts @@ -1036,7 +1036,7 @@ export class ApplicationService { if (caseActivityType === CaseActivityType.APPROVED) { notificationTemplate = NotificationTemplate.APPLICATION_APPROVED; - subject = `onRouteBC Permit Application ${application?.applicationNumber} Approved`; + subject = `onRouteBC Permit Application ${application?.applicationNumber} for Plate ${permitData?.vehicleDetails?.plate} Approved`; notificationData = { applicationNumber: application?.applicationNumber, companyName: application?.company?.legalName, From 1da4384afe584934484eae600780303ed7651a41 Mon Sep 17 00:00:00 2001 From: praju-aot Date: Mon, 9 Sep 2024 16:48:33 -0400 Subject: [PATCH 3/3] Create notification event --- .../case-management.service.ts | 69 +++++++++++++++++++ .../application/application.service.ts | 13 +++- 2 files changed, 80 insertions(+), 2 deletions(-) diff --git a/vehicles/src/modules/case-management/case-management.service.ts b/vehicles/src/modules/case-management/case-management.service.ts index 66b21ecc2..12fb49d88 100644 --- a/vehicles/src/modules/case-management/case-management.service.ts +++ b/vehicles/src/modules/case-management/case-management.service.ts @@ -764,4 +764,73 @@ export class CaseManagementService { } } } + + /** + * The method creates a notification event. + * + * @param currentUser - The current user executing the withdrawal action. + * @param queryRunner - Optional, existing QueryRunner instance used in the transaction process. + * @param caseId - Optional, the ID of the case to be withdrawn. Can be used to retrieve the existing case. + * @param originalCaseId - Optional, the original ID of the case to be withdrawn. Useful in lookup scenarios. + * @param applicationId - Optional, the ID of the permit application associated with the case. + * @param existingCase - Optional, the pre-loaded `Case` entity, if + */ + @LogAsyncMethodExecution() + async createNotificationEvent({ + currentUser, + queryRunner, + caseId, + originalCaseId, + applicationId, + existingCase, + }: { + currentUser: IUserJWT; + queryRunner?: Nullable; + caseId?: Nullable; + originalCaseId?: Nullable; + applicationId?: Nullable; + existingCase?: Nullable; + }): Promise { + let localQueryRunner = true; + ({ localQueryRunner, queryRunner } = await getQueryRunner({ + queryRunner, + dataSource: this.dataSource, + })); + try { + if (!existingCase) { + existingCase = await this.findLatest({ + queryRunner, + caseId, + originalCaseId, + applicationId, + }); + } + + let newEvent = this.createEvent( + existingCase, + CaseEventType.NOTIFICATION, + currentUser, + ); + newEvent = await queryRunner.manager.save(newEvent); + + if (localQueryRunner) { + await queryRunner.commitTransaction(); + } + return await this.classMapper.mapAsync( + newEvent, + CaseEvent, + ReadCaseEvenDto, + ); + } catch (error) { + if (localQueryRunner) { + await queryRunner.rollbackTransaction(); + } + this.logger.error(error); + throw error; + } finally { + if (localQueryRunner) { + await queryRunner.release(); + } + } + } } diff --git a/vehicles/src/modules/permit-application-payment/application/application.service.ts b/vehicles/src/modules/permit-application-payment/application/application.service.ts index 991dee10c..552abcaea 100644 --- a/vehicles/src/modules/permit-application-payment/application/application.service.ts +++ b/vehicles/src/modules/permit-application-payment/application/application.service.ts @@ -1066,13 +1066,22 @@ export class ApplicationService { data: notificationData, }; - void this.dopsService.notificationWithDocumentsFromDops( + await this.dopsService.notificationWithDocumentsFromDops( currentUser, notificationDocument, - true, + false, ); + + await this.caseManagementService.createNotificationEvent({ + currentUser, + applicationId, + queryRunner, + }); + + await queryRunner.commitTransaction(); } } catch (error) { + await queryRunner.rollbackTransaction(); this.logger.error(error); //Swallow Notification error }