Fix billing + transaction tests
MODIFY
src/integration/billing/stripe/StripeBillingIntegration.ts
src/integration/billing/stripe/StripeBillingIntegration.ts
@@ -330,7 +330,8 @@ export default class StripeBillingIntegration extends BillingIntegration<StripeB
message: 'Invoice item not provided',
});
}
- return await this.stripe.invoiceItems.create({
+ try {
+ const stripeInvoiceItem = await this.stripe.invoiceItems.create({
customer: user.billingData.customerID,
currency: this.settings.currency.toLocaleLowerCase(),
amount: invoiceItem.amount,
@@ -339,6 +340,19 @@ export default class StripeBillingIntegration extends BillingIntegration<StripeB
}, {
idempotency_key: idempotencyKey ? idempotencyKey.toString() : null
});
+ const invoice = await BillingStorage.getInvoiceByBillingInvoiceID(this.tenantID, invoiceID);
+ invoice.nbrOfItems++;
+ await BillingStorage.saveInvoice(this.tenantID, invoice);
+ return stripeInvoiceItem;
+ } catch (e) {
+ throw new BackendError({
+ source: Constants.CENTRAL_SERVER,
+ action: ServerAction.BILLING_CREATE_INVOICE_ITEM,
+ module: MODULE_NAME, method: 'createInvoiceItem',
+ message: 'Failed to create invoice item',
+ detailedMessages: { error: e.message, stack: e.stack }
+ });
+ }
}
public async downloadInvoiceDocument(invoice: BillingInvoice): Promise<BillingInvoiceDocument> {

MODIFY
src/utils/Constants.ts
src/utils/Constants.ts
@@ -7,7 +7,7 @@ import Tenant from '../types/Tenant';
export default class Constants {
public static readonly CSV_SEPARATOR = '\t'; // Cannot store regex in enum
- public static readonly AXIOS_TIMEOUT = 60000;
+ public static readonly AXIOS_TIMEOUT = 1000000;
public static readonly DB_RECORD_COUNT_DEFAULT = 100;
public static readonly DB_RECORD_COUNT_CEIL = 2000;

MODIFY
test/api/BillingTest.ts
test/api/BillingTest.ts
@@ -39,6 +39,7 @@ class TestData {
public chargingStationContext: ChargingStationContext;
public transactionUserService: CentralServerService;
public createdUsers: User[] = [];
+ public createdTransactions: number[] = [];
public isForcedSynchro: boolean;
public pending = false;
@@ -121,6 +122,11 @@ describe('Billing Service', function() {
testData.centralUserContext
);
testData.isForcedSynchro = false;
+ testData.siteContext = testData.tenantContext.getSiteContext(ContextDefinition.SITE_CONTEXTS.SITE_WITH_OTHER_USER_STOP_AUTHORIZATION);
+ testData.siteAreaContext = testData.siteContext.getSiteAreaContext(ContextDefinition.SITE_AREA_CONTEXTS.WITH_ACL);
+ testData.chargingStationContext = testData.siteAreaContext.getChargingStationContext(ContextDefinition.CHARGING_STATION_CONTEXTS.ASSIGNED_OCPP16);
+ testData.transactionUserService = new CentralServerService(
+ testData.tenantContext.getTenant().subdomain, testData.userContext);
});
describe('Where admin user', () => {
@@ -290,6 +296,42 @@ describe('Billing Service', function() {
expect(response.data).containSubset(Constants.REST_RESPONSE_SUCCESS);
});
+ it('should add an item to the existing invoice after a transaction', async () => {
+ await testData.userService.billingApi.forceSynchronizeUser({ id: testData.userContext.id });
+ let response = await testData.userService.billingApi.readAll({ Status: BillingInvoiceStatus.DRAFT, UserID: [testData.userContext.id] }, TestConstants.DEFAULT_PAGING, [{ field: 'createdOn', direction: 'desc' }], '/client/api/BillingUserInvoices');
+ const itemsBefore: number = response.data.result[0].nbrOfItems;
+ testData.createdTransactions.push(await generateTransaction(testData.userContext, testData.chargingStationContext));
+ await testData.userService.billingApi.synchronizeInvoices({});
+ response = await testData.userService.billingApi.readAll({ Status: BillingInvoiceStatus.DRAFT, UserID: [testData.userContext.id] }, TestConstants.DEFAULT_PAGING, [{ field: 'createdOn', direction: 'desc' }], '/client/api/BillingUserInvoices');
+ const itemsAfter = response.data.result[0].nbrOfItems;
+ expect(itemsAfter).to.be.eq(itemsBefore + 1);
+ });
+
+ it('should synchronize 1 invoice after a transaction', async () => {
+ await testData.userService.billingApi.synchronizeInvoices({});
+ testData.createdTransactions.push(await generateTransaction(testData.userContext, testData.chargingStationContext));
+ const response = await testData.userService.billingApi.synchronizeInvoices({});
+ expect(response.data).containSubset(Constants.REST_RESPONSE_SUCCESS);
+ expect(response.data.inSuccess).to.be.eq(1);
+ });
+
+ it('should not delete a transaction linked to an invoice', async () => {
+ const transactionID = await generateTransaction(testData.userContext, testData.chargingStationContext);
+ testData.createdTransactions.push(transactionID);
+ const transactionDeleted = await testData.transactionUserService.transactionApi.delete(transactionID);
+ expect(transactionDeleted.data.inError).to.be.eq(1);
+ expect(transactionDeleted.data.inSuccess).to.be.eq(0);
+ });
+
+ it('Should set a transaction in error', async () => {
+ await TestData.setBillingSystemInvalidCredentials(testData);
+ const transactionID = await generateTransaction(testData.userContext, testData.chargingStationContext);
+ expect(transactionID).to.not.be.null;
+ testData.createdTransactions.push(transactionID);
+ const transactions = await testData.transactionUserService.transactionApi.readAllInError({});
+ expect(transactions.data.result.find((transaction) => transaction.id === transactionID)).to.not.be.null;
+ });
+
after(async () => {
await TestData.setBillingSystemValidCredentials(testData);
for (const user of testData.createdUsers) {
@@ -415,98 +457,6 @@ describe('Billing Service', function() {
const downloadResponse = await testData.userService.billingApi.downloadInvoiceDocument({ ID: response.data.result[0].id });
expect(downloadResponse.headers['content-type']).to.be.eq('application/pdf');
});
- });
- });
-
- describe('With component Billing (tenant utall)', () => {
- before(async () => {
- testData.tenantContext = await ContextProvider.defaultInstance.getTenantContext(ContextDefinition.TENANT_CONTEXTS.TENANT_WITH_ALL_COMPONENTS);
- testData.centralUserContext = testData.tenantContext.getUserContext(ContextDefinition.USER_CONTEXTS.DEFAULT_ADMIN);
- testData.userContext = testData.tenantContext.getUserContext(ContextDefinition.USER_CONTEXTS.DEFAULT_ADMIN);
- expect(testData.userContext).to.not.be.null;
- testData.centralUserService = new CentralServerService(
- testData.tenantContext.getTenant().subdomain,
- testData.centralUserContext
- );
- testData.isForcedSynchro = false;
- testData.siteContext = testData.tenantContext.getSiteContext(ContextDefinition.SITE_CONTEXTS.SITE_WITH_OTHER_USER_STOP_AUTHORIZATION);
- testData.siteAreaContext = testData.siteContext.getSiteAreaContext(ContextDefinition.SITE_AREA_CONTEXTS.WITH_ACL);
- testData.chargingStationContext = testData.siteAreaContext.getChargingStationContext(ContextDefinition.CHARGING_STATION_CONTEXTS.ASSIGNED_OCPP16);
- testData.transactionUserService = new CentralServerService(
- testData.tenantContext.getTenant().subdomain, testData.userContext);
- });
-
- describe('Where admin user', () => {
- before(async () => {
- testData.userContext = testData.tenantContext.getUserContext(ContextDefinition.USER_CONTEXTS.DEFAULT_ADMIN);
- assert(testData.userContext, 'User context cannot be null');
- if (testData.userContext === testData.centralUserContext) {
- // Reuse the central user service (to avoid double login)
- testData.userService = testData.centralUserService;
- } else {
- testData.userService = new CentralServerService(
- testData.tenantContext.getTenant().subdomain,
- testData.userContext
- );
- }
- await TestData.setBillingSystemValidCredentials(testData);
- });
-
- it('should add an item to the existing invoice after a transaction', async () => {
- await testData.userService.billingApi.forceSynchronizeUser({ id: testData.userContext.id });
- let response = await testData.userService.billingApi.readAll({ Status: BillingInvoiceStatus.DRAFT, UserID: [testData.userContext.id] }, TestConstants.DEFAULT_PAGING, [{ field: 'createdOn', direction: 'desc' }], '/client/api/BillingUserInvoices');
- const itemsBefore: number = response.data.result[0].nbrOfItems;
- await generateTransaction(testData.userContext, testData.chargingStationContext);
- await testData.userService.billingApi.synchronizeInvoices({});
- response = await testData.userService.billingApi.readAll({ Status: BillingInvoiceStatus.DRAFT, UserID: [testData.userContext.id] }, TestConstants.DEFAULT_PAGING, [{ field: 'createdOn', direction: 'desc' }], '/client/api/BillingUserInvoices');
- const itemsAfter = response.data.result[0].nbrOfItems;
- expect(itemsAfter).to.be.eq(itemsBefore + 1);
- });
-
- it('should synchronize 1 invoice after a transaction', async () => {
- await testData.userService.billingApi.synchronizeInvoices({});
- await generateTransaction(testData.userContext, testData.chargingStationContext);
- const response = await testData.userService.billingApi.synchronizeInvoices({});
- expect(response.data).containSubset(Constants.REST_RESPONSE_SUCCESS);
- expect(response.data.inSuccess).to.be.eq(1);
- });
-
- it('should not delete a transaction linked to an invoice', async () => {
- const transactionID = await generateTransaction(testData.userContext, testData.chargingStationContext);
- const transactionDeleted = await testData.transactionUserService.transactionApi.delete(transactionID);
- expect(transactionDeleted.data.inError).to.be.eq(1);
- expect(transactionDeleted.data.inSuccess).to.be.eq(0);
- });
-
- it('Should set a transaction in error', async () => {
- await TestData.setBillingSystemInvalidCredentials(testData);
- const transactionID = await generateTransaction(testData.userContext, testData.chargingStationContext);
- expect(transactionID).to.not.be.null;
- const transactions = await testData.transactionUserService.transactionApi.readAllInError({});
- expect(transactions.data.result.find((transaction) => transaction.id === transactionID)).to.not.be.null;
- });
- });
-
- describe('Where basic user', () => {
- before(async () => {
- testData.userContext = testData.tenantContext.getUserContext(ContextDefinition.USER_CONTEXTS.DEFAULT_ADMIN);
- testData.userService = new CentralServerService(
- testData.tenantContext.getTenant().subdomain,
- testData.userContext
- );
- await TestData.setBillingSystemValidCredentials(testData);
- testData.userContext = testData.tenantContext.getUserContext(ContextDefinition.USER_CONTEXTS.BASIC_USER);
- assert(testData.userContext, 'User context cannot be null');
- if (testData.userContext === testData.centralUserContext) {
- // Reuse the central user service (to avoid double login)
- testData.userService = testData.centralUserService;
- } else {
- testData.userService = new CentralServerService(
- testData.tenantContext.getTenant().subdomain,
- testData.userContext
- );
- }
- });
it('should create an invoice after a transaction', async () => {
const adminUser = testData.tenantContext.getUserContext(ContextDefinition.USER_CONTEXTS.DEFAULT_ADMIN);
@@ -527,16 +477,16 @@ describe('Billing Service', function() {
await testData.userService.billingApi.synchronizeInvoices({});
let response = await testData.userService.billingApi.readAll({ Status: BillingInvoiceStatus.DRAFT }, TestConstants.DEFAULT_PAGING, [{ field: 'createdOn', direction: 'desc' }], '/client/api/BillingUserInvoices');
const itemsBefore: number = response.data.result[0].nbrOfItems;
- await generateTransaction(testData.userContext, testData.chargingStationContext);
+ testData.createdTransactions.push(await generateTransaction(testData.userContext, testData.chargingStationContext));
await testData.userService.billingApi.synchronizeInvoices({});
response = await testData.userService.billingApi.readAll({ Status: BillingInvoiceStatus.DRAFT }, TestConstants.DEFAULT_PAGING, [{ field: 'createdOn', direction: 'desc' }], '/client/api/BillingUserInvoices');
const itemsAfter = response.data.result[0].nbrOfItems;
expect(itemsAfter).to.be.eq(itemsBefore + 1);
});
});
+ });
after(async () => {
await TestData.setBillingSystemValidCredentials(testData);
});
});
-});

MODIFY
test/api/TransactionCommonTests.ts
test/api/TransactionCommonTests.ts
@@ -5,6 +5,7 @@ import CentralServerService from './client/CentralServerService';
import ChargingStationContext from './context/ChargingStationContext';
import TenantContext from './context/TenantContext';
import TestUtils from './TestUtils';
+import { TransactionInErrorType } from '../../src/types/InError';
import User from '../../src/types/User';
import Utils from '../../src/utils/Utils';
import chaiSubset from 'chai-subset';
@@ -524,7 +525,7 @@ export default class TransactionCommonTests {
transactionId2, tagId, meterStop, stopDate.toDate());
expect(stopTransactionResponse).to.be.transactionStatus('Accepted');
const transactions = await this.transactionUserService.transactionApi.readAllInError(
- { ChargeBoxID: this.chargingStationContext.getChargingStation().id });
+ { ChargeBoxID: this.chargingStationContext.getChargingStation().id, ErrorType: TransactionInErrorType.NO_CONSUMPTION });
expect(transactions.status).to.equal(200);
expect(transactions.data.count).to.equal(2);
expect(transactions.data.result).to.containSubset([{

MODIFY
test/api/context/BillingContext.ts
test/api/context/BillingContext.ts
@@ -66,14 +66,17 @@ export default class BillingContext {
const adminBillingUser = await billingImpl.getUserByEmail(adminUser.email);
const basicBillingUser = await billingImpl.getUserByEmail(basicUser.email);
- for (let i = 0; i < 2; i++) {
- await billingImpl.createInvoice(adminBillingUser, { description: 'TestAdmin' + (i + 1), amount: 100 });
- await billingImpl.createInvoice(basicBillingUser, { description: 'TestBasic' + (i + 1), amount: 100 });
- }
+
+ const adminInvoice = await billingImpl.createInvoice(adminBillingUser, { description: 'TestAdmin 1', amount: 100 });
+ const userInvoice = await billingImpl.createInvoice(basicBillingUser, { description: 'TestBasic 1', amount: 100 });
+ await billingImpl.createInvoiceItem(adminBillingUser, adminInvoice.invoice.invoiceID, { description: 'TestAdmin 2', amount: 100 });
+ await billingImpl.createInvoiceItem(basicBillingUser, userInvoice.invoice.invoiceID, { description: 'TestBasic 2', amount: 100 });
+
let invoice = await billingImpl.createInvoice(adminBillingUser, { description: 'TestAdmin3', amount: 100 });
await billingImpl.sendInvoiceToUser(this.tenantContext.getTenant().id, invoice.invoice);
invoice = await billingImpl.createInvoice(basicBillingUser, { description: 'TestBasic3', amount: 100 });
await billingImpl.sendInvoiceToUser(this.tenantContext.getTenant().id, invoice.invoice);
+ // Await billingImpl.synchronizeInvoices(this.tenantContext.getTenant().id);
}
private async saveBillingSettings(stripeSettings) {

MODIFY
test/api/context/ContextDefinition.ts
test/api/context/ContextDefinition.ts
@@ -310,7 +310,8 @@ export default class ContextDefinition {
taxID: ''
}
}
- }
+ },
+ organization: {},
},
},
{

MODIFY
test/api/ocpp/json/OCPPJsonService16.ts
test/api/ocpp/json/OCPPJsonService16.ts
@@ -2,7 +2,7 @@ import { OCPP15MeterValuesRequest, OCPPAuthorizeRequest, OCPPAuthorizeResponse,
import { MessageType } from '../../../../src/types/WebSocket';
import OCPPService from '../OCPPService';
-import Utils from '../../../utils/Utils';
+import Utils from '../../../../src/utils/Utils';
import WSClient from '../../../../src/client/websocket/WSClient';
import config from '../../../config';
import { performance } from 'perf_hooks';

Fix billing + transaction tests