endpoint to export ocpi consumption data for advenir
MODIFY
src/server/rest/v1/router/api/TransactionRouter.ts
src/server/rest/v1/router/api/TransactionRouter.ts
@@ -19,6 +19,7 @@ export default class TransactionRouter {
this.buildRouteTransactionsGetRefund();
this.buildRouteTransaction();
this.buildRouteTransactionConsumption();
+ this.buildRouteTransactionConsumptionForAdvenir();
this.buildRouteDeleteTransaction();
this.buildRouteDeleteTransactions();
this.buildRoutePushTransactionCDR();
@@ -105,6 +106,13 @@ export default class TransactionRouter {
});
}
+ private buildRouteTransactionConsumptionForAdvenir(): void {
+ this.router.get(`/${RESTServerRoute.REST_TRANSACTION_CONSUMPTIONS_FOR_ADVENIR}`, (req: Request, res: Response, next: NextFunction) => {
+ req.query.TransactionId = req.params.id;
+ void RouterUtils.handleRestServerAction(TransactionService.handleGetTransactionConsumptionForAdvenir.bind(this), ServerAction.TRANSACTION_CONSUMPTION, req, res, next);
+ });
+ }
+
private buildRouteTransactionSoftStop(): void {
this.router.put(`/${RESTServerRoute.REST_TRANSACTION_SOFT_STOP}`, (req: Request, res: Response, next: NextFunction) => {
req.body.ID = req.params.id;

MODIFY
src/server/rest/v1/service/TransactionService.ts
src/server/rest/v1/service/TransactionService.ts
@@ -3,6 +3,7 @@ import ChargingStation, { Connector } from '../../../../types/ChargingStation';
import { HTTPAuthError, HTTPError } from '../../../../types/HTTPError';
import { NextFunction, Request, Response } from 'express';
import Tenant, { TenantComponents } from '../../../../types/Tenant';
+import Transaction, { AdvenirConsumption } from '../../../../types/Transaction';
import { ActionsResponse } from '../../../../types/GlobalType';
import AppAuthError from '../../../../exception/AppAuthError';
@@ -17,20 +18,23 @@ import Configuration from '../../../../utils/Configuration';
import Constants from '../../../../utils/Constants';
import Consumption from '../../../../types/Consumption';
import ConsumptionStorage from '../../../../storage/mongodb/ConsumptionStorage';
+import CpoOCPIClient from '../../../../client/ocpi/CpoOCPIClient';
import { DataResult } from '../../../../types/DataResult';
import { HttpTransactionsRequest } from '../../../../types/requests/HttpTransactionRequest';
import Logging from '../../../../utils/Logging';
import LoggingHelper from '../../../../utils/LoggingHelper';
+import OCPIClientFactory from '../../../../client/ocpi/OCPIClientFactory';
import OCPIFacade from '../../../ocpi/OCPIFacade';
+import { OCPIRole } from '../../../../types/ocpi/OCPIRole';
import OCPPService from '../../../../server/ocpp/services/OCPPService';
import OCPPUtils from '../../../ocpp/utils/OCPPUtils';
import OICPFacade from '../../../oicp/OICPFacade';
import RefundFactory from '../../../../integration/refund/RefundFactory';
import { RefundStatus } from '../../../../types/Refund';
+import RoamingUtils from '../../../../utils/RoamingUtils';
import { ServerAction } from '../../../../types/Server';
import SynchronizeRefundTransactionsTask from '../../../../scheduler/tasks/SynchronizeRefundTransactionsTask';
import TagStorage from '../../../../storage/mongodb/TagStorage';
-import Transaction from '../../../../types/Transaction';
import TransactionStorage from '../../../../storage/mongodb/TransactionStorage';
import TransactionValidator from '../validator/TransactionValidator';
import User from '../../../../types/User';
@@ -540,6 +544,65 @@ export default class TransactionService {
next();
}
+ public static async handleGetTransactionConsumptionForAdvenir(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
+ UtilsService.assertComponentIsActiveFromToken(req.user, TenantComponents.OCPI,
+ Action.READ, Entity.TRANSACTION, MODULE_NAME, 'handleGetTransactionConsumptionForAdvenir');
+ // Filter
+ const filteredRequest = TransactionValidator.getInstance().validateTransactionConsumptionsGetReq(req.query);
+ // Transaction Id is mandatory
+ UtilsService.assertIdIsProvided(action, filteredRequest.TransactionId, MODULE_NAME,
+ 'handleGetConsumptionFromTransaction', req.user);
+ const projectFields = [
+ 'id', 'chargeBox.issuer', 'chargeBox.id', 'chargeBox.issuer', 'chargeBox.public', 'chargeBox.connectors', 'connectorId',
+ ];
+ // Get Transaction
+ const transaction = await TransactionStorage.getTransaction(req.tenant, filteredRequest.TransactionId,
+ { withChargingStation: true }, projectFields);
+ UtilsService.assertObjectExists(action, transaction, `Transaction ID '${filteredRequest.TransactionId}' does not exist`,
+ MODULE_NAME, 'handleGetTransactionConsumptionForAdvenir', req.user);
+ // Check Transaction
+ if (!await Authorizations.canReadTransaction(req.user, transaction)) {
+ throw new AppAuthError({
+ ...LoggingHelper.getTransactionProperties(transaction),
+ errorCode: HTTPAuthError.FORBIDDEN,
+ user: req.user,
+ action: Action.READ, entity: Entity.TRANSACTION,
+ module: MODULE_NAME, method: 'handleGetTransactionConsumptionForAdvenir',
+ value: transaction.id.toString()
+ });
+ }
+ try {
+ const ocpiClient = await OCPIClientFactory.getAvailableOcpiClient(req.tenant, OCPIRole.CPO) as CpoOCPIClient;
+ if (!ocpiClient) {
+ throw new AppError({
+ errorCode: HTTPError.GENERAL_ERROR,
+ message: 'OCPI component requires at least one CPO endpoint to generate Advenir consumption data',
+ module: MODULE_NAME, method: 'handleGetTransactionConsumptionForAdvenir',
+ user: req.user, action
+ });
+ }
+ // Build EvseID
+ const evseID = RoamingUtils.buildEvseID(ocpiClient.getLocalCountryCode(action), ocpiClient.getLocalPartyID(action), transaction.chargeBox, transaction.connectorId);
+ // Get Consumption
+ const consumptions = await ConsumptionStorage.getOptimizedTransactionConsumptions(
+ req.tenant, { transactionId: transaction.id }, [
+ 'startedAt', 'cumulatedConsumptionWh']);
+ const advenirValues = [];
+ for (const consumption of consumptions) {
+ advenirValues.unshift({ timestamp: consumption.startedAt.getTime().toString(), value: consumption.cumulatedConsumptionWh });
+ }
+ const advenirConsumption = {
+ [evseID]: {
+ [transaction.id.toString()]:
+ advenirValues
+ }
+ } as AdvenirConsumption;
+ res.json(advenirConsumption);
+ } catch (error) {
+ await Logging.logActionExceptionMessageAndSendResponse(action, error, req, res, next);
+ }
+ }
+
public static async handleGetTransaction(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
// Filter
const filteredRequest = TransactionValidator.getInstance().validateTransactionGetReq(req.query);

MODIFY
src/types/Server.ts
src/types/Server.ts
@@ -536,6 +536,7 @@ export enum RESTServerRoute {
REST_TRANSACTION_CDR = 'transactions/:id/ocpi/cdr',
REST_TRANSACTION_CDR_EXPORT = 'transactions/:id/ocpi/cdr/export',
REST_TRANSACTION_CONSUMPTIONS = 'transactions/:id/consumptions',
+ REST_TRANSACTION_CONSUMPTIONS_FOR_ADVENIR = 'transactions/:id/consumptions-for-advenir',
REST_TRANSACTION_START = 'transactions/start',
REST_TRANSACTION_STOP = 'transactions/:id/stop',
REST_TRANSACTION_SOFT_STOP = 'transactions/:id/soft-stop',

MODIFY
src/types/Transaction.ts
src/types/Transaction.ts
@@ -42,6 +42,14 @@ export interface UserDefaultTagCar {
errorCodes?: StartTransactionErrorCode[];
}
+export interface AdvenirConsumption {
+ [evseID: string]: {
+ [transactionId: string]: [
+ [{ timestamp: string, value: number }]
+ ]
+ }
+}
+
export enum StartTransactionErrorCode {
BILLING_NO_PAYMENT_METHOD = 'no_payment_method', // start transaction is not possible - user has no payment method
BILLING_NO_TAX = 'billing_no_tax', // start transaction is not possible - the tax ID is not set or inconsistent

endpoint to export ocpi consumption data for advenir