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/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 aeaa42f16..552abcaea 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,72 @@ 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} for Plate ${permitData?.vehicleDetails?.plate} 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, + }; + + await this.dopsService.notificationWithDocumentsFromDops( + currentUser, + notificationDocument, + false, + ); + + await this.caseManagementService.createNotificationEvent({ + currentUser, + applicationId, + queryRunner, + }); + + await queryRunner.commitTransaction(); + } + } catch (error) { + await queryRunner.rollbackTransaction(); + this.logger.error(error); //Swallow Notification error + } + return result; } catch (error) { await queryRunner.rollbackTransaction();