table and multiple tests added
MODIFY
file.txt
file.txt
<mj-column>
<mj-text>
<p>Hello ! User,</p>
- <p>Your session on the charging station SAP Moujins 19 A, connector A has just finished.</p>
+ <p>Your session on the charging station charging box id, connector connector id has just finished.</p>
</mj-text>
</mj-column>
</mj-section>
,<mj-section>
<mj-column width="70%">
<mj-table>
- <tr><th style="font-size:18px;font-weight:300">Consumption</th><td style="font-size:18px;font-weight:400;width:50%;text-align:center">52,3 kW.h</td></tr><tr><th style="font-size:18px;font-weight:300">Total Duration</th><td style="font-size:18px;font-weight:400;width:50%;text-align:center">5h41</td></tr><tr><th style="font-size:18px;font-weight:300">Total Inactivity</th><td style="font-size:18px;font-weight:400;width:50%;text-align:center">0h13</td></tr>
+ <tr><th style="font-size:18px;font-weight:300">Consumption</th><td style="font-size:18px;font-weight:400;width:50%;text-align:center">total consumption kW.h</td></tr><tr><th style="font-size:18px;font-weight:300">Total Duration</th><td style="font-size:18px;font-weight:400;width:50%;text-align:center">undefined</td></tr><tr><th style="font-size:18px;font-weight:300">State of Charge</th><td style="font-size:18px;font-weight:400;width:50%;text-align:center">undefined %</td></tr>
</mj-table>
</mj-column>
</mj-section>
,<mj-section>
<mj-column>
- <mj-button background-color="#8fd365" font-size="16px" href="charging station url">
+ <mj-button background-color="#8fd365" font-size="16px" href="undefined">
View Session
</mj-button>
</mj-column>

MODIFY
src/assets/i18n/en.json
src/assets/i18n/en.json
"title":"Optimal Charge Reached",
"buttonText":"View Session",
"text":["Your electric vehicle, which is connected to {{chargeBoxID}}, connector {{connectorId}}, reached its optimal charge."],
- "tableColumns":["Battery Level"]
+ "table": [
+ { "label": "Battery Level", "value": "{{stateOfCharge}} %" }
+ ]
},
"end-of-session": {
"title":"Session Finished",
"buttonText":"View Session",
"text":["Your session on the charging station {{chargeBoxID}}, connector {{connectorId}} has just finished."],
- "tableColumns":["Consumption","Total Duration","Total Inactivity","Battery Level"]
+ "table": [
+ { "label": "Consumption", "value": "{{totalConsumption}} kW.h" },
+ { "label": "Total Duration", "value": "{{totalDuration}}" },
+ { "label": "Total Inactivity", "value": "{{totalInactivity}}" },
+ { "label": "State of Charge", "value": "{{stateOfCharge}} %" }
+ ]
},
"end-of-charge": {
"title":"Charge Finished",
"buttonText":"View Session",
"text":["Your electric vehicle, which is connected to the charging station {{chargeBoxID}}, connector {{connectorId}}, has just finished charging."],
- "tableColumns":["Consumption","Total Duration","Battery Level"]
+ "table": [
+ { "label": "Consumption", "value": "{{totalConsumption}} kW.h" },
+ { "label": "Total Duration", "value": "{{totalDuration}}" },
+ { "label": "Battery Level", "value": "{{stateOfCharge}} %" }
+ ]
+ },
+ "billing-new-invoice-unpaid": {
+ "title":"new invoice",
+ "buttontext":"View Invoice",
+ "text":["dear {{firstname}}, your invoice {{invoicenumber}} has been paid and is now available. "],
+ "table": [
+ { "label": "Amount Paid", "value": "${{invoiceamount}}$" }
+ ]
+ },
+ "billing-new-invoice-paid": {
+ "title":"new invoice",
+ "buttontext":"Pay Invoice",
+ "text":["A new invoice is available {{invoiceNumber}} Please follow the link below to finalize the payment"],
+ "table": [
+ { "label": "Amount Due", "value": "${{invoiceamount}}" }
+ ]
}
}
}

MODIFY
src/notification/email/EMailNotificationTask.ts
src/notification/email/EMailNotificationTask.ts
@@ -65,21 +65,18 @@ export default class EMailNotificationTask implements NotificationTask {
public async sendOptimalChargeReached(data: OptimalChargeReachedNotification, user: User, tenant: Tenant, severity: NotificationSeverity): Promise<void> {
data.buttonUrl = data.evseDashboardChargingStationURL;
- data.tableValues = [data.stateOfCharge + '%'];
const optionalComponents = await EmailComponentManager.getComponent(EmailComponent.TABLE);
return this.prepareAndSendEmail('optimal-charge-reached', data, user, tenant, severity,false,optionalComponents);
}
public async sendEndOfCharge(data: EndOfChargeNotification, user: User, tenant: Tenant, severity: NotificationSeverity): Promise<void> {
data.buttonUrl = data.evseDashboardChargingStationURL;
- data.tableValues = [data.totalConsumption + ' kW.h',data.totalDuration];
const optionalComponents = await EmailComponentManager.getComponent(EmailComponent.TABLE);
return this.prepareAndSendEmail('end-of-charge', data, user, tenant, severity,false,optionalComponents);
}
public async sendEndOfSession(data: EndOfSessionNotification, user: User, tenant: Tenant, severity: NotificationSeverity): Promise<void> {
data.buttonUrl = data.evseDashboardChargingStationURL;
- data.tableValues = [data.totalConsumption + ' kW.h',data.totalDuration,data.totalInactivity];
const optionalComponents = await EmailComponentManager.getComponent(EmailComponent.TABLE);
return this.prepareAndSendEmail('end-of-session', data, user, tenant, severity,false,optionalComponents);
}
@@ -165,7 +162,12 @@ export default class EMailNotificationTask implements NotificationTask {
}
public async sendBillingNewInvoice(data: BillingNewInvoiceNotification, user: User, tenant: Tenant, severity: NotificationSeverity): Promise<void> {
- return this.prepareAndSendEmail('billing-new-invoice', data, user, tenant, severity);
+ data.buttonUrl = data.evseDashboardInvoiceURL;
+ const optionalComponents = await EmailComponentManager.getComponent(EmailComponent.TABLE);
+ if (data.invoiceStatus === 'paid') {
+ return await this.prepareAndSendEmail('billing-new-invoice-paid', data, user, tenant, severity,false,optionalComponents);
+ }
+ return await this.prepareAndSendEmail('billing-new-invoice-unpaid', data, user, tenant, severity,false,optionalComponents);
}
public async sendCarCatalogSynchronizationFailed(data: CarCatalogSynchronizationFailedNotification, user: User, tenant: Tenant, severity: NotificationSeverity): Promise<void> {

MODIFY
src/notification/email/mjml-template/MjmlTemplate.ts
src/notification/email/mjml-template/MjmlTemplate.ts
@@ -44,9 +44,13 @@ export default class MjmlTemplate {
return;
}
let table = '';
- const tableColumns = i18nManager.translate('email.' + prefix + '.tableColumns');
- for (const [index,tableValue] of context.tableValues.entries()) {
- table = table + `<tr><th style="font-size:18px;font-weight:300">${tableColumns[index]}</th><td style="font-size:18px;font-weight:400;width:50%;text-align:center">${tableValue as string}</td></tr>`;
+ const tableCases = JSON.parse(JSON.stringify(i18nManager.translate('email.' + prefix + '.table')));
+ for (let i = 0; i < tableCases.length;i++) {
+ const tableCase = tableCases[i];
+ const valueRegex = new RegExp(/\{\{([a-zA-Z0-9_.-]*)\}\}/g);
+ const valueSelector = valueRegex.exec(tableCase.value as string);
+ const t = tableCase.value.replace(valueSelector[0],context[valueSelector[1]]);
+ table = table + `<tr><th style="font-size:18px;font-weight:300">${tableCase.label as string}</th><td style="font-size:18px;font-weight:400;width:50%;text-align:center">${t as string}</td></tr>`;
}
this.replace(match[0],table);
}

MODIFY
test/api/EmailTest.ts
test/api/EmailTest.ts
-import { ChargingStationStatusErrorNotification, EndOfChargeNotification, EndOfSessionNotification, EndOfSignedSessionNotification, NewRegisteredUserNotification, NotificationSeverity, OptimalChargeReachedNotification, RequestPasswordNotification } from '../../src/types/UserNotifications';
+import { BillingAccountActivationNotification, BillingAccountCreationLinkNotification, BillingNewInvoiceNotification, ChargingStationRegisteredNotification, ChargingStationStatusErrorNotification, EndOfChargeNotification, EndOfSessionNotification, EndOfSignedSessionNotification, EndUserErrorNotification, NewRegisteredUserNotification, NotificationSeverity, OfflineChargingStationNotification, OptimalChargeReachedNotification, RequestPasswordNotification } from '../../src/types/UserNotifications';
import ContextDefinition from './context/ContextDefinition';
import ContextProvider from './context/ContextProvider';
@@ -61,7 +61,7 @@ describe('Initialization', () => {
await emailNotificationTask.sendRequestPassword(data,user,tenant,severity);
});
- it('Optimal Charge Reached', async () => {
+ it('optimal-charge-reached', async () => {
data = {
transactionId: 1,
siteID: 'site_id',
@@ -78,13 +78,13 @@ describe('Initialization', () => {
await emailNotificationTask.sendOptimalChargeReached(data,user,tenant,severity);
});
- it('End of Charge', async () => {
+ it('end-of-charge', async () => {
data = {
transactionId: 1,
siteID: '',
siteAreaID: 'site area id',
companyID: 'company id',
- chargeBoxID: 'charing box id',
+ chargeBoxID: 'charging box id',
connectorId: 'connector id',
totalConsumption: '48.3',
stateOfCharge: 1,
@@ -96,7 +96,7 @@ describe('Initialization', () => {
await emailNotificationTask.sendEndOfCharge(data,user,tenant,severity);
});
- it('End of Session', async () => {
+ it('end-of-session', async () => {
data = {
transactionId: 1,
siteID: 'site id',
@@ -117,27 +117,74 @@ describe('Initialization', () => {
await emailNotificationTask.sendEndOfSession(data,user,tenant,severity);
});
- // it('End Of Signed Session', async () => {
- // data = {
- // user,
- // alternateUser: user,
- // transactionId: 1,
- // chargeBoxID: 'charging box id',
- // connectorId: 'connector id',
- // tagId: 'tag id',
- // startDate: 'start date',
- // endDate: 'end date',
- // meterStart: 'meter start',
- // meterStop: 'meter stop',
- // totalConsumption: 'total consumption',
- // price: 1,
- // relativeCost: 1,
- // startSignedData: 'start signed data',
- // endSignedData: 'end signed data',
- // evseDashboardURL: 'some_url',
- // } as EndOfSignedSessionNotification;
- // await emailNotificationTask.sendEndOfSession(data,user,tenant,severity);
- // });
+ it('billing-new-invoice', async () => {
+ data = {
+ evseDashboardURL: 'some_url',
+ evseDashboardInvoiceURL: 'some_url',
+ user,
+ invoiceDownloadUrl: 'some_url',
+ payInvoiceUrl: 'some_url',
+ invoiceNumber: '123123123',
+ invoiceAmount: '1200',
+ invoiceStatus: 'active',
+ } as BillingNewInvoiceNotification;
+ await emailNotificationTask.sendBillingNewInvoice(data,user,tenant,severity);
+ });
+
+ it('charging-station-registered', async () => {
+ data = {
+ chargeBoxID: 'Charge Box A1',
+ siteID: 'Site 134',
+ siteAreaID: 'A3',
+ companyID: 'SAP12',
+ evseDashboardURL: 'some_url',
+ evseDashboardChargingStationURL: 'some_url',
+ } as ChargingStationRegisteredNotification;
+ await emailNotificationTask.sendChargingStationRegistered(data,user,tenant,severity);
+ });
+
+ it('end-user-error-notification', async () => {
+ data = {
+ userID: 'some user id',
+ email: 'email',
+ name: 'name',
+ errorTitle: 'error title',
+ errorDescription: 'description',
+ phone: '123123123',
+ evseDashboardURL: 'some_url',
+ } as EndUserErrorNotification;
+ await emailNotificationTask.sendEndUserErrorNotification(data,user,tenant,severity);
+ });
+
+ it('offline-charging-station', async () => {
+ data = {
+ chargeBoxIDs: 'some box id',
+ evseDashboardURL: 'some_url',
+ } as OfflineChargingStationNotification;
+ await emailNotificationTask.sendOfflineChargingStations(data,user,tenant,severity);
+ });
+
+ it('charging-station-charging-error', async () => {
+ data = {
+ chargeBoxID: 'some box id',
+ siteID: 'site id',
+ siteAreaID: 'site area id',
+ companyID: 'company id',
+ connectorId: 'connector id',
+ error: 'this is the error',
+ evseDashboardURL: 'some_url',
+ evseDashboardChargingStationURL: 'some_url',
+ } as ChargingStationStatusErrorNotification;
+ await emailNotificationTask.sendChargingStationStatusError(data,user,tenant,severity);
+ });
+
+ it('billing-account-created', async () => {
+ data = {
+ evseDashboardURL: 'some_url',
+ user,
+ } as BillingAccountActivationNotification;
+ await emailNotificationTask.sendBillingAccountActivationNotification(data,user,tenant,severity);
+ });
});
});

table and multiple tests added