feat(notification): send notification to sub-account owner
MODIFY
src/assets/i18n/en.json
src/assets/i18n/en.json
"title": "New invoice to pay",
"body": "A new invoice ({{invoiceNumber}}) amounting {{amount}} is now available. Please check your email to finalize the payment"
},
+ "billingSubAccountCreationLink": {
+ "title": "Billing sub-account",
+ "body": "You have been invited to create a billing sub-account in organization. Please check your email to complete the process"
+ },
"endUserErrorNotification": {
"title": "A User Reported an Error",
"body": "'{{userName}}' reported: '{{errorTitle}}' - '{{errorDescription}}' in organization '{{tenantName}}'"

ADD
src/assets/server/notification/email/en/billing-subaccount-created.json
+{
+ "subject": "Billing - New invoice",
+ "tenant": "<%- tenantID %>",
+ "baseURL": "<%- evseDashboardURL %>",
+ "body": {
+ "header": {
+ "title": "Billing - Sub-account onboarding",
+ "image": {
+ "left": {
+ "height": 60,
+ "url": "<%- tenantLogoURL %>"
+ },
+ "right": {
+ "height": 50,
+ "url": "<%- evseDashboardURL %>/assets/img/info.png"
+ }
+ }
+ },
+ "beforeActionLines": [
+ "Dear <%- (user.firstName ? user.firstName : user.name) %>,",
+ "",
+ "Your billing sub-account onboarding must be completed by following the link below:"
+ ],
+ "actions": [{
+ "title": "Finish sub-account onboarding",
+ "url": "<%- onboardingLink %>"
+ }],
+ "afterActionLines": [
+ "Best Regards,",
+ "EV Admins."
+ ],
+ "footer": {
+ }
+ }
+}

ADD
src/assets/server/rest/v1/schemas/billing/billing-create-sub-account.json
+{
+ "$id": "billing-create-sub-account",
+ "title": "Create a Billing sub-account Request",
+ "type": "object",
+ "properties": {
+ "userID": {
+ "$ref": "common#/definitions/id"
+ }
+ },
+ "required": [
+ "userID"
+ ]
+}

MODIFY
src/notification/NotificationHandler.ts
src/notification/NotificationHandler.ts
import User, { UserRole } from '../types/User';
-import UserNotifications, { AccountVerificationNotification, AdminAccountVerificationNotification, BillingInvoiceSynchronizationFailedNotification, BillingNewInvoiceNotification, BillingUserSynchronizationFailedNotification, CarCatalogSynchronizationFailedNotification, ChargingStationRegisteredNotification, ChargingStationStatusErrorNotification, ComputeAndApplyChargingProfilesFailedNotification, EndOfChargeNotification, EndOfSessionNotification, EndOfSignedSessionNotification, EndUserErrorNotification, NewRegisteredUserNotification, NotificationSeverity, NotificationSource, OCPIPatchChargingStationsStatusesErrorNotification, OICPPatchChargingStationsErrorNotification, OICPPatchChargingStationsStatusesErrorNotification, OfflineChargingStationNotification, OptimalChargeReachedNotification, PreparingSessionNotStartedNotification, RequestPasswordNotification, SessionNotStartedNotification, TransactionStartedNotification, UnknownUserBadgedNotification, UserAccountInactivityNotification, UserAccountStatusChangedNotification, UserNotificationKeys, VerificationEmailNotification } from '../types/UserNotifications';
+import UserNotifications, { AccountVerificationNotification, AdminAccountVerificationNotification, BillingInvoiceSynchronizationFailedNotification, BillingNewInvoiceNotification, BillingSubAccountCreationLink, BillingUserSynchronizationFailedNotification, CarCatalogSynchronizationFailedNotification, ChargingStationRegisteredNotification, ChargingStationStatusErrorNotification, ComputeAndApplyChargingProfilesFailedNotification, EndOfChargeNotification, EndOfSessionNotification, EndOfSignedSessionNotification, EndUserErrorNotification, NewRegisteredUserNotification, NotificationSeverity, NotificationSource, OCPIPatchChargingStationsStatusesErrorNotification, OICPPatchChargingStationsErrorNotification, OICPPatchChargingStationsStatusesErrorNotification, OfflineChargingStationNotification, OptimalChargeReachedNotification, PreparingSessionNotStartedNotification, RequestPasswordNotification, SessionNotStartedNotification, TransactionStartedNotification, UnknownUserBadgedNotification, UserAccountInactivityNotification, UserAccountStatusChangedNotification, UserNotificationKeys, VerificationEmailNotification } from '../types/UserNotifications';
import ChargingStation from '../types/ChargingStation';
import Configuration from '../utils/Configuration';
@@ -1367,6 +1367,34 @@ export default class NotificationHandler {
}
}
+ public static async sendBillingSubAccountCreationLink(tenant: Tenant, notificationID: string, user: User,
+ sourceData: BillingSubAccountCreationLink): Promise<void> {
+ if (tenant.id !== Constants.DEFAULT_TENANT_ID) {
+ // Get the Tenant logo
+ if (Utils.isNullOrUndefined(tenant.logo) || tenant.logo === '') {
+ const tenantLogo = await TenantStorage.getTenantLogo(tenant);
+ tenant.logo = tenantLogo.logo;
+ }
+ sourceData.tenantLogoURL = tenant.logo;
+ // For each Sources
+ for (const notificationSource of NotificationHandler.notificationSources) {
+ // Active?
+ if (notificationSource.enabled) {
+ try {
+ // Save
+ await NotificationHandler.saveNotification(
+ tenant, notificationSource.channel, notificationID, ServerAction.BILLING_CREATE_SUB_ACCOUNT, { user });
+ // Send
+ void notificationSource.notificationTask.sendBillingSubAccountCreationLink(
+ sourceData, user, tenant, NotificationSeverity.INFO);
+ } catch (error) {
+ await Logging.logActionExceptionMessage(tenant.id, ServerAction.BILLING_CREATE_SUB_ACCOUNT, error);
+ }
+ }
+ }
+ }
+ }
+
private static async saveNotification(tenant: Tenant, channel: string, notificationID: string, sourceDescr: ServerAction,
extraParams: { user?: User, chargingStation?: ChargingStation, notificationData?: any } = {}): Promise<void> {
// Save it

MODIFY
src/notification/NotificationTask.ts
src/notification/NotificationTask.ts
-import { AccountVerificationNotification, AdminAccountVerificationNotification, BillingInvoiceSynchronizationFailedNotification, BillingNewInvoiceNotification, BillingUserSynchronizationFailedNotification, CarCatalogSynchronizationFailedNotification, ChargingStationRegisteredNotification, ChargingStationStatusErrorNotification, ComputeAndApplyChargingProfilesFailedNotification, EndOfChargeNotification, EndOfSessionNotification, EndOfSignedSessionNotification, EndUserErrorNotification, NewRegisteredUserNotification, NotificationSeverity, OCPIPatchChargingStationsStatusesErrorNotification, OICPPatchChargingStationsErrorNotification, OICPPatchChargingStationsStatusesErrorNotification, OfflineChargingStationNotification, OptimalChargeReachedNotification, PreparingSessionNotStartedNotification, RequestPasswordNotification, SessionNotStartedNotification, TransactionStartedNotification, UnknownUserBadgedNotification, UserAccountInactivityNotification, UserAccountStatusChangedNotification, UserCreatePassword, VerificationEmailNotification } from '../types/UserNotifications';
+import { AccountVerificationNotification, AdminAccountVerificationNotification, BillingInvoiceSynchronizationFailedNotification, BillingNewInvoiceNotification, BillingSubAccountCreationLink, BillingUserSynchronizationFailedNotification, CarCatalogSynchronizationFailedNotification, ChargingStationRegisteredNotification, ChargingStationStatusErrorNotification, ComputeAndApplyChargingProfilesFailedNotification, EndOfChargeNotification, EndOfSessionNotification, EndOfSignedSessionNotification, EndUserErrorNotification, NewRegisteredUserNotification, NotificationSeverity, OCPIPatchChargingStationsStatusesErrorNotification, OICPPatchChargingStationsErrorNotification, OICPPatchChargingStationsStatusesErrorNotification, OfflineChargingStationNotification, OptimalChargeReachedNotification, PreparingSessionNotStartedNotification, RequestPasswordNotification, SessionNotStartedNotification, TransactionStartedNotification, UnknownUserBadgedNotification, UserAccountInactivityNotification, UserAccountStatusChangedNotification, UserCreatePassword, VerificationEmailNotification } from '../types/UserNotifications';
import Tenant from '../types/Tenant';
import User from '../types/User';
@@ -33,6 +33,7 @@ export default interface NotificationTask {
sendBillingNewInvoice(data: BillingNewInvoiceNotification, user: User, tenant: Tenant, severity: NotificationSeverity): Promise<void>;
sendBillingNewInvoicePaid(data: BillingNewInvoiceNotification, user: User, tenant: Tenant, severity: NotificationSeverity): Promise<void>;
sendBillingNewInvoiceOpen(data: BillingNewInvoiceNotification, user: User, tenant: Tenant, severity: NotificationSeverity): Promise<void>;
+ sendBillingSubAccountCreationLink(data: BillingSubAccountCreationLink, user: User, tenant: Tenant, severity: NotificationSeverity): Promise<void>;
sendAccountVerificationNotification(data: AccountVerificationNotification, user: User, tenant: Tenant, severity: NotificationSeverity): Promise<void>;
sendAdminAccountVerificationNotification(data: AdminAccountVerificationNotification, user: User, tenant: Tenant, severity: NotificationSeverity): Promise<void>;
sendUserCreatePassword(data: UserCreatePassword, user: User, tenant: Tenant, severity: NotificationSeverity): Promise<void>;

MODIFY
src/notification/email/EMailNotificationTask.ts
src/notification/email/EMailNotificationTask.ts
-import { AccountVerificationNotification, AdminAccountVerificationNotification, BillingInvoiceSynchronizationFailedNotification, BillingNewInvoiceNotification, BillingUserSynchronizationFailedNotification, CarCatalogSynchronizationFailedNotification, ChargingStationRegisteredNotification, ChargingStationStatusErrorNotification, ComputeAndApplyChargingProfilesFailedNotification, EmailNotificationMessage, EndOfChargeNotification, EndOfSessionNotification, EndOfSignedSessionNotification, EndUserErrorNotification, NewRegisteredUserNotification, NotificationSeverity, OCPIPatchChargingStationsStatusesErrorNotification, OICPPatchChargingStationsErrorNotification, OICPPatchChargingStationsStatusesErrorNotification, OfflineChargingStationNotification, OptimalChargeReachedNotification, PreparingSessionNotStartedNotification, RequestPasswordNotification, SessionNotStartedNotification, TransactionStartedNotification, UnknownUserBadgedNotification, UserAccountInactivityNotification, UserAccountStatusChangedNotification, UserCreatePassword, VerificationEmailNotification } from '../../types/UserNotifications';
+import { AccountVerificationNotification, AdminAccountVerificationNotification, BillingInvoiceSynchronizationFailedNotification, BillingNewInvoiceNotification, BillingSubAccountCreationLink, BillingUserSynchronizationFailedNotification, CarCatalogSynchronizationFailedNotification, ChargingStationRegisteredNotification, ChargingStationStatusErrorNotification, ComputeAndApplyChargingProfilesFailedNotification, EmailNotificationMessage, EndOfChargeNotification, EndOfSessionNotification, EndOfSignedSessionNotification, EndUserErrorNotification, NewRegisteredUserNotification, NotificationSeverity, OCPIPatchChargingStationsStatusesErrorNotification, OICPPatchChargingStationsErrorNotification, OICPPatchChargingStationsStatusesErrorNotification, OfflineChargingStationNotification, OptimalChargeReachedNotification, PreparingSessionNotStartedNotification, RequestPasswordNotification, SessionNotStartedNotification, TransactionStartedNotification, UnknownUserBadgedNotification, UserAccountInactivityNotification, UserAccountStatusChangedNotification, UserCreatePassword, VerificationEmailNotification } from '../../types/UserNotifications';
import { Message, SMTPClient, SMTPError } from 'emailjs';
import BackendError from '../../exception/BackendError';
@@ -140,6 +140,10 @@ export default class EMailNotificationTask implements NotificationTask {
return this.prepareAndSendEmail('billing-periodic-operation-failed', data, user, tenant, severity);
}
+ public async sendBillingSubAccountCreationLink(data: BillingSubAccountCreationLink, user: User, tenant: Tenant, severity: NotificationSeverity): Promise<void> {
+ return this.prepareAndSendEmail('billing-subaccount-created', data, user, tenant, severity);
+ }
+
// TODO : Delete sendBillingNewInvoice ??
public async sendBillingNewInvoice(data: BillingNewInvoiceNotification, user: User, tenant: Tenant, severity: NotificationSeverity): Promise<void> {
return this.prepareAndSendEmail('billing-new-invoice', data, user, tenant, severity);

MODIFY
src/notification/remote-push-notification/RemotePushNotificationTask.ts
src/notification/remote-push-notification/RemotePushNotificationTask.ts
-import { AccountVerificationNotification, BillingInvoiceSynchronizationFailedNotification, BillingNewInvoiceNotification, BillingPeriodicOperationFailedNotification, BillingUserSynchronizationFailedNotification, CarCatalogSynchronizationFailedNotification, ChargingStationRegisteredNotification, ChargingStationStatusErrorNotification, ComputeAndApplyChargingProfilesFailedNotification, EndOfChargeNotification, EndOfSessionNotification, EndOfSignedSessionNotification, EndUserErrorNotification, NewRegisteredUserNotification, NotificationSeverity, OCPIPatchChargingStationsStatusesErrorNotification, OICPPatchChargingStationsErrorNotification, OICPPatchChargingStationsStatusesErrorNotification, OfflineChargingStationNotification, OptimalChargeReachedNotification, PreparingSessionNotStartedNotification, RequestPasswordNotification, SessionNotStartedNotification, TransactionStartedNotification, UnknownUserBadgedNotification, UserAccountInactivityNotification, UserAccountStatusChangedNotification, UserNotificationType, VerificationEmailNotification } from '../../types/UserNotifications';
+import { AccountVerificationNotification, BillingInvoiceSynchronizationFailedNotification, BillingNewInvoiceNotification, BillingPeriodicOperationFailedNotification, BillingSubAccountCreationLink, BillingUserSynchronizationFailedNotification, CarCatalogSynchronizationFailedNotification, ChargingStationRegisteredNotification, ChargingStationStatusErrorNotification, ComputeAndApplyChargingProfilesFailedNotification, EndOfChargeNotification, EndOfSessionNotification, EndOfSignedSessionNotification, EndUserErrorNotification, NewRegisteredUserNotification, NotificationSeverity, OCPIPatchChargingStationsStatusesErrorNotification, OICPPatchChargingStationsErrorNotification, OICPPatchChargingStationsStatusesErrorNotification, OfflineChargingStationNotification, OptimalChargeReachedNotification, PreparingSessionNotStartedNotification, RequestPasswordNotification, SessionNotStartedNotification, TransactionStartedNotification, UnknownUserBadgedNotification, UserAccountInactivityNotification, UserAccountStatusChangedNotification, UserNotificationType, VerificationEmailNotification } from '../../types/UserNotifications';
import User, { UserStatus } from '../../types/User';
import Configuration from '../../utils/Configuration';
@@ -341,6 +341,18 @@ export default class RemotePushNotificationTask implements NotificationTask {
title, body, user, { 'error': data.nbrInvoicesInError.toString() }, severity);
}
+ public async sendBillingSubAccountCreationLink(data: BillingSubAccountCreationLink, user: User, tenant: Tenant, severity: NotificationSeverity): Promise<void> {
+ // Set the locale
+ const i18nManager = I18nManager.getInstanceForLocale(user.locale);
+ // Get Message Text
+ const title = i18nManager.translate('notifications.billingPeriodicOperationFailed.title');
+ const body = i18nManager.translate('notifications.billingPeriodicOperationFailed.body',
+ { onboardingLink: data.onboardingLink, tenantName: tenant.name });
+ // Send Notification
+ return this.sendRemotePushNotificationToUser(tenant, UserNotificationType.BILLING_CREATE_SUB_ACCOUNT,
+ title, body, user, { 'onboardingLink': data.onboardingLink }, severity);
+ }
+
public async sendComputeAndApplyChargingProfilesFailed(data: ComputeAndApplyChargingProfilesFailedNotification,
user: User, tenant: Tenant, severity: NotificationSeverity): Promise<void> {
// Set the locale

MODIFY
src/types/UserNotifications.ts
src/types/UserNotifications.ts
@@ -71,6 +71,7 @@ export enum UserNotificationType {
BILLING_INVOICE_SYNCHRONIZATION_FAILED = 'BillingInvoiceSynchronizationFailed',
BILLING_PERIODIC_OPERATION_FAILED = 'BillingPeriodicOperationFailed',
BILLING_NEW_INVOICE = 'BillingNewInvoice',
+ BILLING_CREATE_SUB_ACCOUNT = 'BillingCreateSubAccount',
CAR_CATALOG_SYNCHRONIZATION_FAILED = 'CarCatalogSynchronizationFailed',
CHECK_AND_APPLY_SMART_CHARGING_FAILED = 'ComputeAndApplyChargingProfilesFailed',
SESSION_NOT_STARTED_AFTER_AUTHORIZE = 'SessionNotStartedAfterAuthorize',
@@ -295,6 +296,10 @@ export interface BillingNewInvoiceNotification extends BaseNotification {
invoiceStatus: string;
}
+export interface BillingSubAccountCreationLink extends BaseNotification {
+ onboardingLink: string;
+}
+
export interface CarCatalogSynchronizationFailedNotification extends BaseNotification {
nbrCarsInError: number;
evseDashboardURL: string;

feat(notification): send notification to sub-account owner