bp - transfer - read
ADD
src/assets/server/rest/v1/schemas/billing/billing-transfer-get.json
+{
+ "$id": "billing-transfer-get",
+ "title": "Get transfer Request",
+ "type": "object",
+ "properties": {
+ "ID": {
+ "$ref": "common#/definitions/id"
+ }
+ },
+ "required": [
+ "ID"
+ ]
+}

MODIFY
src/authorization/AuthorizationsDefinition.ts
src/authorization/AuthorizationsDefinition.ts
@@ -509,7 +509,7 @@ export const AUTHORIZATION_DEFINITION: AuthorizationDefinition = {
},
{
resource: Entity.BILLING_TRANSFER,
- action: [Action.LIST, Action.BILLING_FINALIZE_TRANSFER, Action.BILLING_SEND_TRANSFER],
+ action: [Action.LIST, Action.READ, Action.BILLING_FINALIZE_TRANSFER, Action.BILLING_SEND_TRANSFER],
attributes: [
'id', 'status', 'createdOn', 'sessions', 'totalAmount', 'transferAmount', 'accountID', 'transferExternalID',
'account.businessOwnerID', 'account.accountExternalID', 'businessOwner.name', 'businessOwner.firstName',

MODIFY
src/server/rest/v1/router/api/BillingRouter.ts
src/server/rest/v1/router/api/BillingRouter.ts
@@ -48,6 +48,7 @@ export default class BillingRouter {
this.buildRouteBillingGetAccount();
this.buildRouteBillingSendAccountOnboarding();
this.buildRouteBillingGetTransfers();
+ this.buildRouteBillingGetTransfer();
this.buildRouteBillingFinalizeTransfer();
this.buildRouteBillingSendTransfer();
return this.router;
@@ -181,6 +182,13 @@ export default class BillingRouter {
});
}
+ private buildRouteBillingGetTransfer(): void {
+ this.router.get(`/${RESTServerRoute.REST_BILLING_TRANSFER}`, (req: Request, res: Response, next: NextFunction) => {
+ req.query.ID = req.params.id;
+ void RouterUtils.handleRestServerAction(BillingService.handleGetTransfer.bind(this), ServerAction.BILLING_TRANSFER, req, res, next);
+ });
+ }
+
private buildRouteBillingFinalizeTransfer(): void {
this.router.patch(`/${RESTServerRoute.REST_BILLING_TRANSFER_FINALIZE}`, (req: Request, res: Response, next: NextFunction) => {
req.params.ID = req.params.id;

MODIFY
src/server/rest/v1/service/AuthorizationService.ts
src/server/rest/v1/service/AuthorizationService.ts
import { Action, AuthorizationActions, AuthorizationContext, AuthorizationFilter, Entity } from '../../../../types/Authorization';
import { AssetDataResult, BillingAccountsDataResult, BillingInvoiceDataResult, BillingPaymentMethodDataResult, BillingTaxDataResult, BillingTransfersDataResult, CarCatalogDataResult, CarDataResult, ChargingProfileDataResult, ChargingStationDataResult, CompanyDataResult, DataResult, LogDataResult, PricingDefinitionDataResult, RegistrationTokenDataResult, SiteAreaDataResult, SiteDataResult, TagDataResult, UserDataResult } from '../../../../types/DataResult';
-import { BillingAccount, BillingInvoice, BillingPaymentMethod, BillingTax } from '../../../../types/Billing';
+import { BillingAccount, BillingInvoice, BillingPaymentMethod, BillingTax, BillingTransfer } from '../../../../types/Billing';
import { Car, CarCatalog } from '../../../../types/Car';
import { ChargePointStatus, OCPPProtocol, OCPPVersion } from '../../../../types/ocpp/OCPPServer';
import { HttpAssetGetRequest, HttpAssetsGetRequest } from '../../../../types/requests/HttpAssetRequest';
-import { HttpBillingAccountGetRequest, HttpBillingAccountsGetRequest, HttpBillingInvoiceRequest, HttpBillingInvoicesRequest, HttpBillingTransfersGetRequest, HttpDeletePaymentMethod, HttpPaymentMethods, HttpSetupPaymentMethod } from '../../../../types/requests/HttpBillingRequest';
+import { HttpBillingAccountGetRequest, HttpBillingAccountsGetRequest, HttpBillingInvoiceRequest, HttpBillingInvoicesRequest, HttpBillingTransferGetRequest, HttpBillingTransfersGetRequest, HttpDeletePaymentMethod, HttpPaymentMethods, HttpSetupPaymentMethod } from '../../../../types/requests/HttpBillingRequest';
import { HttpCarCatalogGetRequest, HttpCarCatalogsGetRequest, HttpCarGetRequest, HttpCarsGetRequest } from '../../../../types/requests/HttpCarRequest';
import { HttpChargingProfileRequest, HttpChargingProfilesGetRequest, HttpChargingStationGetRequest, HttpChargingStationsGetRequest } from '../../../../types/requests/HttpChargingStationRequest';
import { HttpCompaniesGetRequest, HttpCompanyGetRequest } from '../../../../types/requests/HttpCompanyRequest';
@@ -823,7 +823,7 @@ export default class AuthorizationService {
return authorizations;
}
- public static async checkAndGetBillingTransfersAuthorizations(tenant: Tenant, userToken: UserToken, authAction: Action,
+ public static async checkAndGetTransfersAuthorizations(tenant: Tenant, userToken: UserToken, authAction: Action,
filteredRequest?: Partial<HttpBillingTransfersGetRequest>, failsWithException = true): Promise<AuthorizationFilter> {
const authorizations: AuthorizationFilter = {
filters: {},
@@ -837,6 +837,12 @@ export default class AuthorizationService {
return authorizations;
}
+ public static async checkAndGetTransferAuthorizations(tenant: Tenant, userToken: UserToken,
+ filteredRequest: Partial<HttpBillingTransferGetRequest>, authAction: Action, entityData?: EntityData): Promise<AuthorizationFilter> {
+ return AuthorizationService.checkAndGetEntityAuthorizations(
+ tenant, Entity.BILLING_TRANSFER, userToken, filteredRequest, filteredRequest.ID ? { TransferID: filteredRequest.ID } : {}, authAction, entityData);
+ }
+
public static async checkAndGetTaxesAuthorizations(tenant: Tenant, userToken: UserToken, failsWithException = true): Promise<AuthorizationFilter> {
const authorizations: AuthorizationFilter = {
filters: {},
@@ -905,6 +911,15 @@ export default class AuthorizationService {
Utils.removeCanPropertiesWithFalseValue(billingInvoice);
}
+ // eslint-disable-next-line @typescript-eslint/require-await
+ public static async addTransferAuthorizations(tenant: Tenant, userToken: UserToken, billingTransfer: BillingTransfer, authorizationFilter: AuthorizationFilter): Promise<void> {
+ billingTransfer.canRead = true; // Always true as it should be filtered upfront
+ // billingTransfer.canDownload = await AuthorizationService.canPerformAuthorizationAction(
+ // tenant, userToken, Entity.TRANSFER, Action.DOWNLOAD, authorizationFilter, billingTransfer.businessOwnerID ? { UserID: billingTransfer.businessOwnerID } : {}, billingTransfer);
+ // Optimize data over the net
+ Utils.removeCanPropertiesWithFalseValue(billingTransfer);
+ }
+
public static async addAccountsAuthorizations(tenant: Tenant, userToken: UserToken, billingAccounts: BillingAccountsDataResult,
authorizationFilter: AuthorizationFilter): Promise<void> {
// Add Meta Data

MODIFY
src/server/rest/v1/service/BillingService.ts
src/server/rest/v1/service/BillingService.ts
@@ -706,7 +706,7 @@ export default class BillingService {
// Filter
const filteredRequest = BillingValidatorRest.getInstance().validateBillingTransfersGetReq(req.query);
// Check auth
- const authorizations = await AuthorizationService.checkAndGetBillingTransfersAuthorizations(req.tenant, req.user, Action.LIST, filteredRequest /* , false */);
+ const authorizations = await AuthorizationService.checkAndGetTransfersAuthorizations(req.tenant, req.user, Action.LIST, filteredRequest /* , false */);
if (!authorizations.authorized) {
UtilsService.sendEmptyDataResult(res, next);
return;
@@ -731,13 +731,25 @@ export default class BillingService {
next();
}
+ public static async handleGetTransfer(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
+ // Check if component is active
+ UtilsService.assertComponentIsActiveFromToken(req.user, TenantComponents.BILLING_PLATFORM,
+ Action.READ, Entity.BILLING_TRANSFER, MODULE_NAME, 'handleGetTransfer');
+ // Filter
+ const filteredRequest = BillingValidatorRest.getInstance().validateBillingTransferGetReq(req.query);
+ // Check and Get Company
+ const transfer = await UtilsService.checkAndGetTransferAuthorization(req.tenant, req.user, filteredRequest.ID, Action.READ, action, null, {}, true);
+ res.json(transfer);
+ next();
+ }
+
public static async handleFinalizeTransfer(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
// Check if component is active
UtilsService.assertComponentIsActiveFromToken(req.user, TenantComponents.BILLING_PLATFORM,
Action.BILLING_FINALIZE_TRANSFER, Entity.BILLING_TRANSFER, MODULE_NAME, 'handleFinalizeTransfer');
const filteredRequest = BillingValidatorRest.getInstance().validateBillingTransferFinalizeReq(req.params);
// Check authorization
- await AuthorizationService.checkAndGetBillingTransfersAuthorizations(req.tenant, req.user, Action.BILLING_FINALIZE_TRANSFER);
+ await AuthorizationService.checkAndGetTransfersAuthorizations(req.tenant, req.user, Action.BILLING_FINALIZE_TRANSFER);
// Get the billing implementation
const billingImpl = await BillingFactory.getBillingImpl(req.tenant);
if (!billingImpl) {
@@ -750,7 +762,7 @@ export default class BillingService {
});
}
// Get the transfer
- let transfer = await BillingStorage.getTransferByID(req.tenant, filteredRequest.ID);
+ let transfer = await BillingStorage.getTransfer(req.tenant, filteredRequest.ID);
UtilsService.assertObjectExists(action, transfer, `Transfer ID '${filteredRequest.ID}' does not exist`, MODULE_NAME, 'handleFinalizeTransfer', req.user);
// Check if the transfer is in draft status
if (transfer.status !== BillingTransferStatus.DRAFT) {
@@ -793,7 +805,7 @@ export default class BillingService {
Action.BILLING_SEND_TRANSFER, Entity.BILLING_TRANSFER, MODULE_NAME, 'handleSendTransferInvoice');
const filteredRequest = BillingValidatorRest.getInstance().validateBillingTransferSendReq(req.params);
// Check authorization
- await AuthorizationService.checkAndGetBillingTransfersAuthorizations(req.tenant, req.user, Action.BILLING_SEND_TRANSFER);
+ await AuthorizationService.checkAndGetTransfersAuthorizations(req.tenant, req.user, Action.BILLING_SEND_TRANSFER);
// Get the billing implementation
const billingImpl = await BillingFactory.getBillingImpl(req.tenant);
if (!billingImpl) {
@@ -806,7 +818,7 @@ export default class BillingService {
});
}
// Get the transfer
- let transfer = await BillingStorage.getTransferByID(req.tenant, filteredRequest.ID);
+ let transfer = await BillingStorage.getTransfer(req.tenant, filteredRequest.ID);
UtilsService.assertObjectExists(action, transfer, `Transfer ID '${filteredRequest.ID}' does not exist`, MODULE_NAME, 'handleSendTransfer', req.user);
// Check if the transfer is in draft status
if (transfer.status !== BillingTransferStatus.FINALIZED) {

MODIFY
src/server/rest/v1/service/UtilsService.ts
src/server/rest/v1/service/UtilsService.ts
import { Action, AuthorizationFilter, Entity } from '../../../../types/Authorization';
-import { BillingAccount, BillingInvoice } from '../../../../types/Billing';
+import { BillingAccount, BillingInvoice, BillingTransfer } from '../../../../types/Billing';
import { Car, CarCatalog } from '../../../../types/Car';
import ChargingStation, { ChargePoint, Command } from '../../../../types/ChargingStation';
import { EntityData, URLInfo } from '../../../../types/GlobalType';
@@ -26,6 +26,7 @@ import CompanyStorage from '../../../../storage/mongodb/CompanyStorage';
import Constants from '../../../../utils/Constants';
import Cypher from '../../../../utils/Cypher';
import { DataResult } from '../../../../types/DataResult';
+import { HttpBillingTransferGetRequest } from '../../../../types/requests/HttpBillingRequest';
import { Log } from '../../../../types/Log';
import LogStorage from '../../../../storage/mongodb/LogStorage';
import Logging from '../../../../utils/Logging';
@@ -839,6 +840,47 @@ export default class UtilsService {
return invoice;
}
+ public static async checkAndGetTransferAuthorization(tenant: Tenant, userToken: UserToken, ID: string, authAction: Action,
+ action: ServerAction, entityData?: EntityData, additionalFilters: Record<string, any> = {}, applyProjectFields = false): Promise<BillingTransfer> {
+ // Check mandatory fields
+ UtilsService.assertIdIsProvided(action, ID, MODULE_NAME, 'checkAndGetTransferAuthorization', userToken);
+ // Get dynamic auth
+ const authorizations = await AuthorizationService.checkAndGetTransferAuthorizations(
+ tenant, userToken, { ID }, authAction, entityData);
+ // Get Invoice
+ const transfer = await BillingStorage.getTransfer(tenant, ID,
+ // TODO - authorizations
+ // {
+ // ...additionalFilters,
+ // ...authorizations.filters
+ // },
+ applyProjectFields ? authorizations.projectFields : null
+ );
+ UtilsService.assertObjectExists(action, transfer, `Transfer ID '${ID}' does not exist`,
+ MODULE_NAME, 'checkAndGetTransferAuthorization', userToken);
+ // Assign projected fields
+ if (authorizations.projectFields && applyProjectFields) {
+ transfer.projectFields = authorizations.projectFields;
+ }
+ // Assign Metadata
+ if (authorizations.metadata) {
+ transfer.metadata = authorizations.metadata;
+ }
+ // Add Actions
+ await AuthorizationService.addTransferAuthorizations(tenant, userToken, transfer, authorizations);
+ const authorized = AuthorizationService.canPerformAction(transfer, authAction);
+ if (!authorized) {
+ throw new AppAuthError({
+ errorCode: HTTPAuthError.FORBIDDEN,
+ user: userToken,
+ action: authAction, entity: Entity.BILLING_TRANSFER,
+ module: MODULE_NAME, method: 'checkAndGetTransferAuthorization',
+ value: ID
+ });
+ }
+ return transfer;
+ }
+
public static async checkAndGetTagAuthorization(tenant: Tenant, userToken:UserToken, tagID: string, authAction: Action,
action: ServerAction, entityData?: EntityData, additionalFilters: Record<string, any> = {}, applyProjectFields = false): Promise<Tag> {
return UtilsService.checkAndGetTagByXXXAuthorization(tenant, userToken, tagID, TagStorage.getTag.bind(this),

MODIFY
src/server/rest/v1/validator/BillingValidatorRest.ts
src/server/rest/v1/validator/BillingValidatorRest.ts
-import { HttpBillingInvoiceRequest, HttpBillingInvoicesRequest, HttpBillingAccountActivateRequest, HttpBillingAccountCreateRequest, HttpBillingAccountGetRequest, HttpBillingAccountsGetRequest, HttpBillingTransferFinalizeRequest, HttpBillingTransferSendRequest, HttpBillingTransfersGetRequest, HttpDeletePaymentMethod, HttpPaymentMethods, HttpSetupPaymentMethod } from '../../../../types/requests/HttpBillingRequest';
+import { HttpBillingAccountActivateRequest, HttpBillingAccountCreateRequest, HttpBillingAccountGetRequest, HttpBillingAccountsGetRequest, HttpBillingInvoiceRequest, HttpBillingInvoicesRequest, HttpBillingTransferFinalizeRequest, HttpBillingTransferGetRequest, HttpBillingTransferSendRequest, HttpBillingTransfersGetRequest, HttpDeletePaymentMethod, HttpPaymentMethods, HttpSetupPaymentMethod } from '../../../../types/requests/HttpBillingRequest';
import { BillingSettings } from '../../../../types/Setting';
import Schema from '../../../../types/validator/Schema';
@@ -19,6 +19,7 @@ export default class BillingValidatorRest extends SchemaValidator {
private billingAccountsGet: Schema = JSON.parse(fs.readFileSync(`${global.appRoot}/assets/server/rest/v1/schemas/billing/billing-accounts-get.json`, 'utf8'));
private billingAccountGet: Schema = JSON.parse(fs.readFileSync(`${global.appRoot}/assets/server/rest/v1/schemas/billing/billing-account-get.json`, 'utf8'));
private billingTransfersGet: Schema = JSON.parse(fs.readFileSync(`${global.appRoot}/assets/server/rest/v1/schemas/billing/billing-transfers-get.json`, 'utf8'));
+ private billingTransferGet: Schema = JSON.parse(fs.readFileSync(`${global.appRoot}/assets/server/rest/v1/schemas/billing/billing-transfer-get.json`, 'utf8'));
private billingTransferFinalize: Schema = JSON.parse(fs.readFileSync(`${global.appRoot}/assets/server/rest/v1/schemas/billing/billing-transfer-finalize.json`, 'utf8'));
private billingTransferSend: Schema = JSON.parse(fs.readFileSync(`${global.appRoot}/assets/server/rest/v1/schemas/billing/billing-transfer-send.json`, 'utf8'));
@@ -77,6 +78,10 @@ export default class BillingValidatorRest extends SchemaValidator {
return this.validate(this.billingTransfersGet, data);
}
+ public validateBillingTransferGetReq(data: Record<string, unknown>): HttpBillingTransferGetRequest {
+ return this.validate(this.billingTransferGet, data);
+ }
+
public validateBillingTransferFinalizeReq(data: Record<string, unknown>): HttpBillingTransferFinalizeRequest {
return this.validate(this.billingTransferFinalize, data);
}

MODIFY
src/storage/mongodb/BillingStorage.ts
src/storage/mongodb/BillingStorage.ts
@@ -513,7 +513,7 @@ export default class BillingStorage {
};
}
- public static async getTransferByID(tenant: Tenant, id: string, projectFields?: string[]): Promise<BillingTransfer> {
+ public static async getTransfer(tenant: Tenant, id: string, projectFields?: string[]): Promise<BillingTransfer> {
const transferMDB = await BillingStorage.getTransfers(tenant, {
IDs: [id]
}, Constants.DB_PARAMS_SINGLE_RECORD, projectFields);

MODIFY
src/types/Authorization.ts
src/types/Authorization.ts
@@ -95,7 +95,7 @@ export enum Entity {
NOTIFICATION = 'Notification',
TAG = 'Tag',
PAYMENT_METHOD = 'PaymentMethod',
- SOURCE = 'Source',
+ SOURCE = 'Source'
}
export enum Action {

MODIFY
src/types/GlobalType.ts
src/types/GlobalType.ts
-import { BillingAccount, BillingInvoice, BillingPaymentMethod, BillingTax } from './Billing';
+import { BillingAccount, BillingInvoice, BillingPaymentMethod, BillingTax, BillingTransfer } from './Billing';
import { Car, CarCatalog } from './Car';
import ChargingStation, { Connector } from './ChargingStation';
@@ -85,7 +85,7 @@ export enum ImportStatus {
ERROR = 'E',
}
-export type EntityData = Car|User|Company|Site|SiteArea|Tag|CarCatalog|ChargingStation|PricingDefinition|Log|RegistrationToken|BillingInvoice|BillingPaymentMethod|BillingTax|Setting|BillingAccount|ChargingProfile|Connector;
+export type EntityData = Car|User|Company|Site|SiteArea|Tag|CarCatalog|ChargingStation|PricingDefinition|Log|RegistrationToken|BillingInvoice|BillingPaymentMethod|BillingTax|Setting|BillingAccount|BillingTransfer|ChargingProfile|Connector;
interface TSGlobal extends Global {
database: MongoDBStorage;

MODIFY
src/types/Server.ts
src/types/Server.ts
@@ -460,6 +460,7 @@ export enum ServerAction {
BILLING_ACCOUNT = 'BillingAccount',
BILLING_ACCOUNTS = 'BillingAccounts',
BILLING_TRANSFERS = 'BillingTransfers',
+ BILLING_TRANSFER = 'BillingTransfer',
BILLING_TRANSFER_FINALIZE = 'BillingTransferFinalize',
BILLING_TRANSFER_SEND = 'BillingTransferSend',
@@ -704,6 +705,7 @@ export enum RESTServerRoute {
REST_BILLING_DOWNLOAD_INVOICE = 'invoices/:invoiceID/download',
REST_BILLING_TRANSFERS = 'billing/transfers',
+ REST_BILLING_TRANSFER = 'billing/transfers/:id',
REST_BILLING_TRANSFER_FINALIZE = 'billing/transfers/:id/finalize',
REST_BILLING_TRANSFER_SEND = 'billing/transfers/:id/send',

MODIFY
src/types/requests/HttpBillingRequest.ts
src/types/requests/HttpBillingRequest.ts
@@ -63,6 +63,10 @@ export interface HttpBillingTransfersGetRequest extends HttpDatabaseRequest {
Search?: string;
}
+export interface HttpBillingTransferGetRequest extends HttpByIDRequest {
+ ID: string;
+}
+
export interface HttpBillingTransferFinalizeRequest extends HttpByIDRequest {
ID: string;
}

MODIFY
test/api/BillingPlatformTest.ts
test/api/BillingPlatformTest.ts
@@ -214,7 +214,7 @@ describeif(isBillingProperlyConfigured)('Billing', () => {
transfer.id = transferID;
const finalizeResponse = await billingTestHelper.getCurrentUserService().billingApi.finalizeTransfer(transferID);
expect(finalizeResponse.status).to.be.eq(StatusCodes.OK);
- const finalizedTransfer = await BillingStorage.getTransferByID(billingTestHelper.getTenant(), transferID);
+ const finalizedTransfer = await BillingStorage.getTransfer(billingTestHelper.getTenant(), transferID);
expect(finalizedTransfer.status).to.eq(BillingTransferStatus.FINALIZED);
});
@@ -295,7 +295,10 @@ describeif(isBillingProperlyConfigured)('Billing', () => {
// The user should have no DRAFT invoices
const nbDraftInvoices = await billingTestHelper.checkForDraftInvoices();
assert(nbDraftInvoices === 0, 'The expected number of DRAFT invoices is not correct');
- // TODO - check the generated transfer
+ // Check Transfer Data for the current account ID
+ const nbDraftTransfers = await billingTestHelper.checkForDraftTransfers();
+ assert(nbDraftTransfers === 1, 'The expected number of DRAFT transfers is not correct');
+ await billingTestHelper.finalizeDraftTransfer();
});
});
@@ -317,7 +320,10 @@ describeif(isBillingProperlyConfigured)('Billing', () => {
// The user should have no DRAFT invoices
const nbDraftInvoices = await billingTestHelper.checkForDraftInvoices();
assert(nbDraftInvoices === 0, 'The expected number of DRAFT invoices is not correct');
- // TODO - check the generated transfer
+ // Check Transfer Data for the current account ID
+ const nbDraftTransfers = await billingTestHelper.checkForDraftTransfers();
+ assert(nbDraftTransfers === 1, 'The expected number of DRAFT transfers is not correct');
+ await billingTestHelper.finalizeDraftTransfer();
});
});
@@ -402,7 +408,7 @@ describeif(isBillingProperlyConfigured)('Billing', () => {
const transferID = await BillingStorage.saveTransfer(billingTestHelper.getTenant(), transfer);
expect(transferID).to.not.be.null;
- const retrievedTransfer = await BillingStorage.getTransferByID(billingTestHelper.getTenant(), transferID);
+ const retrievedTransfer = await BillingStorage.getTransfer(billingTestHelper.getTenant(), transferID);
expect(retrievedTransfer).to.containSubset(transfer);
});

MODIFY
test/api/BillingTestHelper.ts
test/api/BillingTestHelper.ts
/* eslint-disable @typescript-eslint/member-ordering */
/* eslint-disable max-len */
import AsyncTask, { AsyncTaskStatus } from '../../src/types/AsyncTask';
-import { BillingAccount, BillingAccountStatus, BillingDataTransactionStop, BillingInvoice, BillingInvoiceStatus, BillingStatus, BillingUser } from '../../src/types/Billing';
+import { BillingAccount, BillingAccountStatus, BillingDataTransactionStop, BillingInvoice, BillingInvoiceStatus, BillingStatus, BillingTransfer, BillingTransferStatus, 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';
@@ -988,4 +988,41 @@ export default class BillingTestHelper {
}
return this.billingAccount;
}
+
+ public async checkForDraftTransfers(): Promise<number> {
+ const result = await this.getDraftTransfers();
+ return result.length;
+ }
+
+ public async getDraftTransfers() : Promise<any> {
+ const accountID = this.billingAccount?.id;
+ assert(accountID, 'accountID should not be null - make sure the context has been initialized with ==> billingTestHelper.initContext2TestConnectedAccounts()');
+ const params = { Status: BillingInvoiceStatus.DRAFT, AccountID: [accountID] };
+ const paging = TestConstants.DEFAULT_PAGING;
+ const ordering = [{ field: '-createdOn' }];
+ const response = await this.adminUserService.billingApi.readTransfers(params, paging, ordering);
+ return response?.data?.result;
+ }
+
+ public async getLatestDraftTransfer(): Promise<BillingTransfer> {
+ const draftTransfers = await this.getDraftTransfers();
+ return (draftTransfers && draftTransfers.length > 0) ? draftTransfers[0] : null;
+ }
+
+ public async getNumberOfSessionsInTransfer(): Promise<number> {
+ // ACHTUNG: There is no data after running: npm run mochatest:createContext
+ // In that situation we return 0!
+ const draftTransfer = await this.getLatestDraftTransfer();
+ return (draftTransfer) ? draftTransfer.sessions?.length : 0;
+ }
+
+ public async finalizeDraftTransfer(): Promise<void> {
+ const transfer = await this.getLatestDraftTransfer();
+ assert(transfer?.id, 'transfer ID should not be null');
+ let response = await this.getCurrentUserService().billingApi.finalizeTransfer(transfer.id);
+ expect(response.status).to.be.eq(StatusCodes.OK);
+ response = await this.adminUserService.billingApi.readTransfer(transfer.id);
+ expect(response.status).to.be.eq(StatusCodes.OK);
+ expect(response.data.status).to.eq(BillingTransferStatus.FINALIZED);
+ }
}

MODIFY
test/api/client/BillingApi.ts
test/api/client/BillingApi.ts
@@ -84,6 +84,10 @@ export default class BillingApi extends CrudApi {
return super.readAll(params, paging, ordering, super.buildRestEndpointUrl(RESTServerRoute.REST_BILLING_TRANSFERS));
}
+ public async readTransfer(id: string) {
+ return super.read({}, super.buildRestEndpointUrl(RESTServerRoute.REST_BILLING_TRANSFER, { id }));
+ }
+
public async finalizeTransfer(id) {
return super.patch({}, super.buildRestEndpointUrl(RESTServerRoute.REST_BILLING_TRANSFER_FINALIZE, { id }));
}

bp - transfer - read