test(billing-platform): reduced amount of sub-accounts created
MODIFY
src/server/rest/v1/service/BillingService.ts
src/server/rest/v1/service/BillingService.ts
@@ -664,6 +664,19 @@ export default class BillingService {
user: req.user
});
}
+ // Get the targeted sub account
+ const subAccount = await BillingStorage.getSubAccountByID(req.tenant, transfer.accountID);
+ UtilsService.assertObjectExists(action, subAccount, `Sub account ID '${transfer.accountID}' does not exist`, MODULE_NAME, 'handleSendTransferInvoice', req.user);
+ // Get the sub account owner
+ const user = await UserStorage.getUser(req.tenant, subAccount.businessOwnerID);
+ UtilsService.assertObjectExists(action, user, `User ID '${transfer.accountID}' does not exist`, MODULE_NAME, 'handleSendTransferInvoice', req.user);
+ // Synchronize owner if needed
+ if (!user.billingData || !user.billingData.customerID) {
+ user.billingData = (await billingImpl.forceSynchronizeUser(user)).billingData;
+ await UserStorage.saveUser(req.tenant, user);
+ }
+ await billingImpl.generateTransferInvoice(transfer, user);
+ // Update the transfer status
transfer.status = BillingTransferStatus.FINALIZED;
await BillingStorage.saveTransfer(req.tenant, transfer);
res.json(Constants.REST_RESPONSE_SUCCESS);
@@ -674,7 +687,7 @@ export default class BillingService {
// Check if component is active
UtilsService.assertComponentIsActiveFromToken(req.user, TenantComponents.BILLING_PLATFORM,
Action.BILLING_SEND_TRANSFER, Entity.BILLING_TRANSFER, MODULE_NAME, 'handleSendTransferInvoice');
- const filteredRequest = BillingValidatorRest.getInstance().validateBillingTransferFinalizeReq(req.params);
+ const filteredRequest = BillingValidatorRest.getInstance().validateBillingTransferSendReq(req.params);
// Check authorization
await AuthorizationService.checkAndGetBillingTransfersAuthorizations(req.tenant, req.user, Action.BILLING_SEND_TRANSFER);
// Get the billing implementation
@@ -701,15 +714,9 @@ export default class BillingService {
user: req.user
});
}
- // Get the targeted sub account
- const subAccount = await BillingStorage.getSubAccountByID(req.tenant, transfer.accountID);
- UtilsService.assertObjectExists(action, subAccount, `Sub account ID '${transfer.accountID}' does not exist`, MODULE_NAME, 'handleSendTransferInvoice', req.user);
- // Get the sub account owner
- const user = await UserStorage.getUser(req.tenant, subAccount.businessOwnerID);
- UtilsService.assertObjectExists(action, user, `User ID '${transfer.accountID}' does not exist`, MODULE_NAME, 'handleSendTransferInvoice', req.user);
- // Synchronize owner and generate invoice
- user.billingData = (await billingImpl.forceSynchronizeUser(user)).billingData;
- await billingImpl.generateTransferInvoice(transfer, user);
+ transfer.status = BillingTransferStatus.TRANSFERRED;
+ await BillingStorage.saveTransfer(req.tenant, transfer);
+ // TODO - send an email notification with the invoice
res.json(Constants.REST_RESPONSE_SUCCESS);
next();
}

MODIFY
test/api/BillingTest.ts
test/api/BillingTest.ts
@@ -990,18 +990,7 @@ describeif(isBillingProperlyConfigured)('Billing', () => {
it('should create a sub account and activate it', async () => {
// Create a sub account
- const response = await billingTestHelper.userService.billingApi.createSubAccount({
- businessOwnerID: billingTestHelper.userContext.id
- });
- expect(response.status).to.be.eq(StatusCodes.CREATED);
- // Send the activation link
- const subAccountOnboardResponse = await billingTestHelper.userService.billingApi.sendSubAccountOnboarding(response.data.id);
- expect(subAccountOnboardResponse.status).to.be.eq(StatusCodes.OK);
- expect(subAccountOnboardResponse.data.status).to.be.eq(BillingAccountStatus.PENDING);
- // Activate the sub account
- const activationResponse = await billingTestHelper.userService.billingApi.activateSubAccount({ accountID: response.data.id, TenantID: billingTestHelper.tenantContext.getTenant().id });
- expect(activationResponse.status).to.be.eq(StatusCodes.OK);
- expect(activationResponse.data.status).to.be.eq(BillingAccountStatus.ACTIVE);
+ await billingTestHelper.createActivatedSubAccount();
});
it('should not activate an inexistent sub account', async () => {
@@ -1011,45 +1000,29 @@ describeif(isBillingProperlyConfigured)('Billing', () => {
it('should not activate a sub account twice', async () => {
// Create a sub account
- const response = await billingTestHelper.userService.billingApi.createSubAccount({
- businessOwnerID: billingTestHelper.userContext.id
- });
- expect(response.status).to.be.eq(StatusCodes.CREATED);
- // Send the activation link
- const subAccountOnboardResponse = await billingTestHelper.userService.billingApi.sendSubAccountOnboarding(response.data.id);
- expect(subAccountOnboardResponse.status).to.be.eq(StatusCodes.OK);
- // Activate the sub account
- let activationResponse = await billingTestHelper.userService.billingApi.activateSubAccount({ accountID: response.data.id, TenantID: billingTestHelper.tenantContext.getTenant().id });
- expect(activationResponse.status).to.be.eq(StatusCodes.OK);
- activationResponse = await billingTestHelper.userService.billingApi.activateSubAccount({ accountID: response.data.id, TenantID: billingTestHelper.tenantContext.getTenant().id });
+ const subAccount = await billingTestHelper.getSubAccount();
+ const activationResponse = await billingTestHelper.userService.billingApi.activateSubAccount({ accountID: subAccount.id, TenantID: billingTestHelper.tenantContext.getTenant().id });
expect(activationResponse.status).to.be.eq(StatusCodes.INTERNAL_SERVER_ERROR);
});
it('should create a company assigned to a sub-account', async () => {
- const subAccountResponse = await billingTestHelper.userService.billingApi.createSubAccount({
- businessOwnerID: billingTestHelper.userContext.id
- });
- expect(subAccountResponse.status).to.be.eq(StatusCodes.CREATED);
-
+ const subAccount = await billingTestHelper.getSubAccount();
const platformFeeStrategy = BillingPlatformFeeStrategyFactory.build();
let companyResponse = await billingTestHelper.userService.companyApi.create({
...CompanyFactory.build(),
accountData: {
- accountID: subAccountResponse.data.id,
+ accountID: subAccount.id,
platformFeeStrategy
}
});
expect(companyResponse.status).to.be.eq(StatusCodes.OK);
companyResponse = await billingTestHelper.userService.companyApi.readById(companyResponse.data.id);
- expect(companyResponse.data.accountData.accountID).to.eq(subAccountResponse.data.id);
+ expect(companyResponse.data.accountData.accountID).to.eq(subAccount.id);
expect(companyResponse.data.accountData.platformFeeStrategy).to.deep.eq(platformFeeStrategy);
});
it('should update a company to assign a sub-account', async () => {
- const subAccountResponse = await billingTestHelper.userService.billingApi.createSubAccount({
- businessOwnerID: billingTestHelper.userContext.id
- });
- expect(subAccountResponse.status).to.be.eq(StatusCodes.CREATED);
+ const subAccount = await billingTestHelper.getSubAccount();
let companyResponse = await billingTestHelper.userService.companyApi.create(CompanyFactory.build());
expect(companyResponse.status).to.be.eq(StatusCodes.OK);
@@ -1059,22 +1032,19 @@ describeif(isBillingProperlyConfigured)('Billing', () => {
id: companyID,
...CompanyFactory.build(),
accountData: {
- accountID: subAccountResponse.data.id,
+ accountID: subAccount.id,
platformFeeStrategy
}
});
expect(companyResponse.status).to.be.eq(StatusCodes.OK);
companyResponse = await billingTestHelper.userService.companyApi.readById(companyID);
- expect(companyResponse.data.accountData.accountID).to.eq(subAccountResponse.data.id);
+ expect(companyResponse.data.accountData.accountID).to.eq(subAccount.id);
expect(companyResponse.data.accountData.platformFeeStrategy).to.deep.eq(platformFeeStrategy);
});
it('should create a site assigned to a sub-account', async () => {
- const subAccountResponse = await billingTestHelper.userService.billingApi.createSubAccount({
- businessOwnerID: billingTestHelper.userContext.id
- });
- expect(subAccountResponse.status).to.be.eq(StatusCodes.CREATED);
+ const subAccount = await billingTestHelper.getSubAccount();
// Create a company
const companyResponse = await billingTestHelper.userService.companyApi.create(CompanyFactory.build());
@@ -1085,21 +1055,18 @@ describeif(isBillingProperlyConfigured)('Billing', () => {
...SiteFactory.build(),
companyID: companyResponse.data.id,
accountData: {
- accountID: subAccountResponse.data.id,
+ accountID: subAccount.id,
platformFeeStrategy
}
});
expect(siteResponse.status).to.be.eq(StatusCodes.OK);
siteResponse = await billingTestHelper.userService.siteApi.readById(siteResponse.data.id);
- expect(siteResponse.data.accountData.accountID).to.eq(subAccountResponse.data.id);
+ expect(siteResponse.data.accountData.accountID).to.eq(subAccount.id);
expect(siteResponse.data.accountData.platformFeeStrategy).to.deep.eq(platformFeeStrategy);
});
it('should update a site to assign a sub-account', async () => {
- const subAccountResponse = await billingTestHelper.userService.billingApi.createSubAccount({
- businessOwnerID: billingTestHelper.userContext.id
- });
- expect(subAccountResponse.status).to.be.eq(StatusCodes.CREATED);
+ const subAccount = await billingTestHelper.getSubAccount();
// Create a company
const companyResponse = await billingTestHelper.userService.companyApi.create(CompanyFactory.build());
@@ -1118,45 +1085,39 @@ describeif(isBillingProperlyConfigured)('Billing', () => {
...SiteFactory.build(),
companyID: companyResponse.data.id,
accountData: {
- accountID: subAccountResponse.data.id,
+ accountID: subAccount.id,
platformFeeStrategy
}
});
expect(siteResponse.status).to.be.eq(StatusCodes.OK);
siteResponse = await billingTestHelper.userService.siteApi.readById(siteID);
- expect(siteResponse.data.accountData.accountID).to.eq(subAccountResponse.data.id);
+ expect(siteResponse.data.accountData.accountID).to.eq(subAccount.id);
expect(siteResponse.data.accountData.platformFeeStrategy).to.deep.eq(platformFeeStrategy);
});
it('should list sub-accounts', async () => {
- const subAccountResponse = await billingTestHelper.userService.billingApi.createSubAccount({
- businessOwnerID: billingTestHelper.userContext.id
- });
- expect(subAccountResponse.status).to.be.eq(StatusCodes.CREATED);
+ const subAccount = await billingTestHelper.getSubAccount();
// List sub-accounts
const subAccountsResponse = await billingTestHelper.userService.billingApi.readSubAccounts({
userID: billingTestHelper.userContext.id,
- ID: subAccountResponse.data.id
+ ID: subAccount.id
});
expect(subAccountsResponse.status).to.be.eq(StatusCodes.OK);
- expect(subAccountsResponse.data.result.map((subAccount: BillingAccount) => subAccount.id)).to.include(subAccountResponse.data.id);
+ expect(subAccountsResponse.data.result.map((account: BillingAccount) => account.id)).to.include(subAccount.id);
});
it('should read sub-account', async () => {
- const subAccountCreateResponse = await billingTestHelper.userService.billingApi.createSubAccount({
- businessOwnerID: billingTestHelper.userContext.id
- });
- expect(subAccountCreateResponse.status).to.be.eq(StatusCodes.CREATED);
+ const subAccount = await billingTestHelper.getSubAccount();
// Get the sub-account
- const subAccountResponse = await billingTestHelper.userService.billingApi.readSubAccount(subAccountCreateResponse.data.id);
- const subAccount = subAccountResponse.data as BillingAccount;
+ const subAccountResponse = await billingTestHelper.userService.billingApi.readSubAccount(subAccount.id);
+ const account = subAccountResponse.data as BillingAccount;
expect(subAccountResponse.status).to.be.eq(StatusCodes.OK);
- expect(subAccount.id).to.be.eq(subAccountCreateResponse.data.id);
- expect(subAccount.businessOwnerID).to.be.eq(billingTestHelper.userContext.id);
- expect(subAccount.status).to.be.eq(BillingAccountStatus.IDLE);
+ expect(account.id).to.be.eq(subAccount.id);
+ expect(account.businessOwnerID).to.be.eq(billingTestHelper.userContext.id);
+ expect(account.status).to.be.eq(BillingAccountStatus.ACTIVE);
});
it('should send sub-account onboarding', async () => {
@@ -1222,8 +1183,8 @@ describeif(isBillingProperlyConfigured)('Billing', () => {
});
it('should finalize transfer', async () => {
- const transfer = BillingTransferFactory.build();
- transfer.status = BillingTransferStatus.DRAFT;
+ const subAccount = await billingTestHelper.getSubAccount();
+ const transfer: BillingTransfer = { ...BillingTransferFactory.build(), accountID: subAccount.id, status: BillingTransferStatus.DRAFT };
const transferID = await BillingStorage.saveTransfer(billingTestHelper.tenantContext.getTenant(), transfer);
transfer.id = transferID;
const finalizeResponse = await billingTestHelper.userService.billingApi.finalizeTransfer(transferID);
@@ -1241,22 +1202,9 @@ describeif(isBillingProperlyConfigured)('Billing', () => {
expect(finalizeResponse.status).to.be.eq(StatusCodes.INTERNAL_SERVER_ERROR);
});
- it('should generate a transfer invoice', async () => {
- const response = await billingTestHelper.userService.billingApi.createSubAccount({
- businessOwnerID: billingTestHelper.userContext.id
- });
- expect(response.status).to.be.eq(StatusCodes.CREATED);
- // Send the activation link
- const subAccountOnboardResponse = await billingTestHelper.userService.billingApi.sendSubAccountOnboarding(response.data.id);
- expect(subAccountOnboardResponse.status).to.be.eq(StatusCodes.OK);
- expect(subAccountOnboardResponse.data.status).to.be.eq(BillingAccountStatus.PENDING);
- // Activate the sub account
- const activationResponse = await billingTestHelper.userService.billingApi.activateSubAccount({ accountID: response.data.id, TenantID: billingTestHelper.tenantContext.getTenant().id });
- expect(activationResponse.status).to.be.eq(StatusCodes.OK);
- expect(activationResponse.data.status).to.be.eq(BillingAccountStatus.ACTIVE);
-
-
- const transfer: BillingTransfer = { ...BillingTransferFactory.build(), status: BillingTransferStatus.DRAFT, accountID: activationResponse.data.id };
+ it('should send a transfer', async () => {
+ const subAccount = await billingTestHelper.createActivatedSubAccount();
+ const transfer: BillingTransfer = { ...BillingTransferFactory.build(), status: BillingTransferStatus.DRAFT, accountID: subAccount.id };
transfer.id = await BillingStorage.saveTransfer(billingTestHelper.tenantContext.getTenant(), transfer);
const finalizeResponse = await billingTestHelper.userService.billingApi.finalizeTransfer(transfer.id);
expect(finalizeResponse.status).to.be.eq(StatusCodes.OK);

MODIFY
test/api/BillingTestHelper.ts
test/api/BillingTestHelper.ts
/* eslint-disable max-len */
import AsyncTask, { AsyncTaskStatus } from '../../src/types/AsyncTask';
-import { BillingAccount, BillingDataTransactionStop, BillingInvoice, BillingInvoiceStatus, BillingStatus, BillingUser } from '../../src/types/Billing';
+import { BillingAccount, BillingAccountStatus, BillingDataTransactionStop, BillingInvoice, BillingInvoiceStatus, BillingStatus, BillingUser } from '../../src/types/Billing';
import { BillingSettings, BillingSettingsType, SettingDB } from '../../src/types/Setting';
import { ChargePointErrorCode, ChargePointStatus, OCPPStatusNotificationRequest } from '../../src/types/ocpp/OCPPServer';
import ChargingStation, { ConnectorType } from '../../src/types/ChargingStation';
@@ -57,6 +57,7 @@ export default class BillingTestHelper {
// Billing Implementation - STRIPE?
public billingImpl: StripeBillingIntegration;
public billingUser: BillingUser; // DO NOT CONFUSE - BillingUser is not a User!
+ public subAccount: BillingAccount;
public async initialize(tenant: string) : Promise<void> {
this.tenantContext = await ContextProvider.defaultInstance.getTenantContext(tenant);
@@ -809,4 +810,27 @@ export default class BillingTestHelper {
assert(response?.data?.id === pricingDefinitionId, 'The ID should be: ' + pricingDefinitionId);
assert(response?.data?.entityName === siteArea.name, 'The Site Area data should be retrieved as well');
}
+
+ public async createActivatedSubAccount(): Promise<BillingAccount> {
+ const response = await this.userService.billingApi.createSubAccount({
+ businessOwnerID: this.userContext.id
+ });
+ expect(response.status).to.be.eq(StatusCodes.CREATED);
+ // Send the activation link
+ const subAccountOnboardResponse = await this.userService.billingApi.sendSubAccountOnboarding(response.data.id);
+ expect(subAccountOnboardResponse.status).to.be.eq(StatusCodes.OK);
+ expect(subAccountOnboardResponse.data.status).to.be.eq(BillingAccountStatus.PENDING);
+ // Activate the sub account
+ const activationResponse = await this.userService.billingApi.activateSubAccount({ accountID: response.data.id, TenantID: this.tenantContext.getTenant().id });
+ expect(activationResponse.status).to.be.eq(StatusCodes.OK);
+ expect(activationResponse.data.status).to.be.eq(BillingAccountStatus.ACTIVE);
+ return activationResponse.data;
+ }
+
+ public async getSubAccount(): Promise<BillingAccount> {
+ if (!this.subAccount) {
+ this.subAccount = await this.createActivatedSubAccount();
+ }
+ return this.subAccount;
+ }
}

test(billing-platform): reduced amount of sub-accounts created