REST endpoint to push the CDR
MODIFY
src/client/ocpi/CpoOCPIClient.ts
src/client/ocpi/CpoOCPIClient.ts
@@ -699,7 +699,6 @@ export default class CpoOCPIClient extends OCPIClient {
// Read configuration to retrieve
const countryCode = this.getLocalCountryCode(ServerAction.OCPI_CHECK_LOCATIONS);
const partyID = this.getLocalPartyID(ServerAction.OCPI_CHECK_LOCATIONS);
-
const locationUrl = locationsUrl + `/${countryCode}/${partyID}/${location.id}`;
// Log
Logging.logDebug({

MODIFY
src/migration/tasks/UpdateChargingStationTemplatesTask.ts
src/migration/tasks/UpdateChargingStationTemplatesTask.ts
@@ -34,7 +34,7 @@ export default class UpdateChargingStationTemplatesTask extends MigrationTask {
}
getVersion(): string {
- return '3.14';
+ return '3.15';
}
private async applyTemplateToChargingStations(tenant: Tenant) {

MODIFY
src/server/ocpp/services/OCPPService.ts
src/server/ocpp/services/OCPPService.ts
@@ -379,7 +379,7 @@ export default class OCPPService {
await ConsumptionStorage.saveConsumption(headers.tenantID, consumption);
}
// Handle OCPI
- await this.processOCPITransaction(headers.tenantID, transaction, chargingStation, TransactionAction.UPDATE);
+ await OCPPUtils.processOCPITransaction(headers.tenantID, transaction, chargingStation, TransactionAction.UPDATE);
// Save Transaction
await TransactionStorage.saveTransaction(headers.tenantID, transaction);
// Update Charging Station
@@ -636,7 +636,7 @@ export default class OCPPService {
// Billing
await OCPPUtils.billTransaction(headers.tenantID, transaction, TransactionAction.START);
// OCPI
- await this.processOCPITransaction(headers.tenantID, transaction, chargingStation, TransactionAction.START);
+ await OCPPUtils.processOCPITransaction(headers.tenantID, transaction, chargingStation, TransactionAction.START);
// Save it
transaction.id = await TransactionStorage.saveTransaction(headers.tenantID, transaction);
// Clean up Charging Station's connector transaction info
@@ -832,7 +832,7 @@ export default class OCPPService {
await ConsumptionStorage.saveConsumption(headers.tenantID, consumption);
}
// OCPI
- await this.processOCPITransaction(headers.tenantID, transaction, chargingStation, TransactionAction.STOP);
+ await OCPPUtils.processOCPITransaction(headers.tenantID, transaction, chargingStation, TransactionAction.STOP);
// Save the transaction
transaction.id = await TransactionStorage.saveTransaction(headers.tenantID, transaction);
// Notify User
@@ -958,7 +958,7 @@ export default class OCPPService {
return;
}
// Check for inactivity
- await this.checkStatusNotificationInactivity(tenantID, chargingStation, statusNotification, foundConnector);
+ await this.checkStatusNotificationInactivityAndPushCdr(tenantID, chargingStation, statusNotification, foundConnector);
// Set connector data
foundConnector.connectorId = statusNotification.connectorId;
foundConnector.status = statusNotification.status;
@@ -1006,7 +1006,7 @@ export default class OCPPService {
}
}
- private async checkStatusNotificationInactivity(tenantID: string, chargingStation: ChargingStation, statusNotification: OCPPStatusNotificationRequestExtended, connector: Connector) {
+ private async checkStatusNotificationInactivityAndPushCdr(tenantID: string, chargingStation: ChargingStation, statusNotification: OCPPStatusNotificationRequestExtended, connector: Connector) {
// Check Inactivity
// OCPP 1.6: Finishing --> Available
if (connector.status === ChargePointStatus.FINISHING &&
@@ -1029,7 +1029,7 @@ export default class OCPPService {
// Build extra inactivity consumption
await OCPPUtils.buildExtraConsumptionInactivity(tenantID, lastTransaction);
// OCPI: post CDR
- await this.processOCPITransaction(tenantID, lastTransaction, chargingStation, TransactionAction.END);
+ await OCPPUtils.processOCPITransaction(tenantID, lastTransaction, chargingStation, TransactionAction.END);
// Save
await TransactionStorage.saveTransaction(tenantID, lastTransaction);
// Log
@@ -1037,7 +1037,7 @@ export default class OCPPService {
tenantID: tenantID,
source: chargingStation.id,
user: lastTransaction.userID,
- module: MODULE_NAME, method: 'checkStatusNotificationInactivity',
+ module: MODULE_NAME, method: 'checkStatusNotificationInactivityAndPushCdr',
action: ServerAction.EXTRA_INACTIVITY,
message: `Connector '${lastTransaction.connectorId}' > Transaction ID '${lastTransaction.id}' > Extra Inactivity of ${lastTransaction.stop.extraInactivitySecs} secs has been added`,
detailedMessages: [statusNotification, lastTransaction]
@@ -1048,7 +1048,7 @@ export default class OCPPService {
tenantID: tenantID,
source: chargingStation.id,
user: lastTransaction.userID,
- module: MODULE_NAME, method: 'checkStatusNotificationInactivity',
+ module: MODULE_NAME, method: 'checkStatusNotificationInactivityAndPushCdr',
action: ServerAction.EXTRA_INACTIVITY,
message: `Connector '${lastTransaction.connectorId}' > Transaction ID '${lastTransaction.id}' > Extra Inactivity has already been computed`,
detailedMessages: [statusNotification, lastTransaction]
@@ -1066,7 +1066,7 @@ export default class OCPPService {
// Marked done
lastTransaction.stop.extraInactivityComputed = true;
// OCPI: post CDR
- await this.processOCPITransaction(tenantID, lastTransaction, chargingStation, TransactionAction.END);
+ await OCPPUtils.processOCPITransaction(tenantID, lastTransaction, chargingStation, TransactionAction.END);
// Save
await TransactionStorage.saveTransaction(tenantID, lastTransaction);
// Log
@@ -1074,7 +1074,7 @@ export default class OCPPService {
tenantID: tenantID,
source: chargingStation.id,
user: lastTransaction.userID,
- module: MODULE_NAME, method: 'checkStatusNotificationInactivity',
+ module: MODULE_NAME, method: 'checkStatusNotificationInactivityAndPushCdr',
action: ServerAction.EXTRA_INACTIVITY,
message: `Connector '${lastTransaction.connectorId}' > Transaction ID '${lastTransaction.id}' > No Extra Inactivity has been added`,
detailedMessages: [statusNotification, lastTransaction]
@@ -1298,100 +1298,6 @@ export default class OCPPService {
}
}
- private async processOCPITransaction(tenantID: string, transaction: Transaction, chargingStation: ChargingStation, transactionAction: TransactionAction) {
- if (!transaction.user || transaction.user.issuer) {
- return;
- }
- const user: User = transaction.user;
- const tenant: Tenant = await TenantStorage.getTenant(tenantID);
- let action: ServerAction;
- switch (transactionAction) {
- case TransactionAction.START:
- action = ServerAction.START_TRANSACTION;
- break;
- case TransactionAction.UPDATE:
- action = ServerAction.UPDATE_TRANSACTION;
- break;
- case TransactionAction.STOP:
- action = ServerAction.STOP_TRANSACTION;
- break;
- }
- if (!Utils.isTenantComponentActive(tenant, TenantComponents.OCPI)) {
- throw new BackendError({
- user: user,
- action: action,
- module: MODULE_NAME,
- method: 'processOCPITransaction',
- message: `Unable to ${transactionAction} transaction for user '${user.id}' not issued locally`
- });
- }
- const ocpiClient = await OCPIClientFactory.getAvailableOcpiClient(tenant, OCPIRole.CPO) as CpoOCPIClient;
- if (!ocpiClient) {
- throw new BackendError({
- user: user,
- action: action,
- module: MODULE_NAME,
- method: 'processOCPITransaction',
- message: `OCPI component requires at least one CPO endpoint to ${transactionAction} transactions`
- });
- }
- let authorizationId;
- let authorizations: DataResult<OCPPAuthorizeRequestExtended>;
- switch (transactionAction) {
- case TransactionAction.START:
- // eslint-disable-next-line no-case-declarations
- const tag = user.tags.find(((value) => value.id === transaction.tagID));
- if (!tag.ocpiToken) {
- throw new BackendError({
- user: user,
- action: action,
- module: MODULE_NAME,
- method: 'processOCPITransaction',
- message: `User '${user.id}' with tag '${transaction.tagID}' cannot ${transactionAction} transaction thought OCPI protocol due to missing ocpiToken`
- });
- }
- // Retrieve Authorization ID
- authorizations = await OCPPStorage.getAuthorizes(tenant.id, {
- dateFrom: moment(transaction.timestamp).subtract(10, 'minutes').toDate(),
- chargeBoxID: transaction.chargeBoxID,
- tagID: transaction.tagID
- }, Constants.DB_PARAMS_MAX_LIMIT);
- // Found ID?
- if (authorizations && authorizations.result && authorizations.result.length > 0) {
- // Get the first non used Authorization OCPI ID
- for (const authorization of authorizations.result) {
- if (authorization.authorizationId) {
- const ocpiTransaction = await TransactionStorage.getOCPITransaction(tenant.id, authorization.authorizationId);
- // OCPI ID not used yet
- if (!ocpiTransaction) {
- authorizationId = authorization.authorizationId;
- break;
- }
- }
- }
- }
- if (!authorizationId) {
- throw new BackendError({
- user: user,
- action: action,
- module: MODULE_NAME, method: 'processOCPITransaction',
- message: `User '${user.id}' with tag '${transaction.tagID}' cannot ${transactionAction} transaction thought OCPI protocol due to missing Authorization`
- });
- }
- await ocpiClient.startSession(tag.ocpiToken, chargingStation, transaction, authorizationId);
- break;
- case TransactionAction.UPDATE:
- await ocpiClient.updateSession(transaction);
- break;
- case TransactionAction.STOP:
- await ocpiClient.stopSession(transaction);
- break;
- case TransactionAction.END:
- await ocpiClient.postCdr(transaction);
- break;
- }
- }
-
private async updateChargingStationWithTransaction(tenantID: string, chargingStation: ChargingStation, transaction: Transaction) {
// Get the connector
const foundConnector: Connector = Utils.getConnectorFromID(chargingStation, transaction.connectorId);

MODIFY
src/server/ocpp/utils/OCPPUtils.ts
src/server/ocpp/utils/OCPPUtils.ts
import { ActionsResponse, KeyValue } from '../../../types/GlobalType';
import { ChargingProfile, ChargingProfilePurposeType } from '../../../types/ChargingProfile';
import ChargingStation, { ChargingStationCapabilities, ChargingStationOcppParameters, ChargingStationTemplate, ConnectorCurrentLimitSource, CurrentType, OcppParameter, StaticLimitAmps, TemplateUpdateResult } from '../../../types/ChargingStation';
+import { OCPPAuthorizeRequestExtended, OCPPMeasurand, OCPPNormalizedMeterValue, OCPPPhase, OCPPReadingContext, OCPPStopTransactionRequestExtended, OCPPUnitOfMeasure } from '../../../types/ocpp/OCPPServer';
import { OCPPChangeConfigurationCommandParam, OCPPChangeConfigurationCommandResult, OCPPChargingProfileStatus, OCPPConfigurationStatus, OCPPGetConfigurationCommandParam, OCPPGetConfigurationCommandResult } from '../../../types/ocpp/OCPPClient';
-import { OCPPMeasurand, OCPPNormalizedMeterValue, OCPPPhase, OCPPReadingContext, OCPPStopTransactionRequestExtended, OCPPUnitOfMeasure } from '../../../types/ocpp/OCPPServer';
import Transaction, { InactivityStatus, TransactionAction, TransactionStop } from '../../../types/Transaction';
import BackendError from '../../../exception/BackendError';
@@ -14,13 +14,20 @@ import ChargingStationVendorFactory from '../../../integration/charging-station-
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 Logging from '../../../utils/Logging';
+import OCPIClientFactory from '../../../client/ocpi/OCPIClientFactory';
+import { OCPIRole } from '../../../types/ocpi/OCPIRole';
import OCPPStorage from '../../../storage/mongodb/OCPPStorage';
import { PricedConsumption } from '../../../types/Pricing';
import PricingFactory from '../../../integration/pricing/PricingFactory';
import { PricingSettingsType } from '../../../types/Setting';
import { ServerAction } from '../../../types/Server';
import SiteArea from '../../../types/SiteArea';
+import Tenant from '../../../types/Tenant';
+import TenantComponents from '../../../types/TenantComponents';
+import TenantStorage from '../../../storage/mongodb/TenantStorage';
import TransactionStorage from '../../../storage/mongodb/TransactionStorage';
import User from '../../../types/User';
import UserToken from '../../../types/UserToken';
@@ -30,6 +37,100 @@ import moment from 'moment';
const MODULE_NAME = 'OCPPUtils';
export default class OCPPUtils {
+ public static async processOCPITransaction(tenantID: string, transaction: Transaction, chargingStation: ChargingStation, transactionAction: TransactionAction) {
+ if (!transaction.user || transaction.user.issuer) {
+ return;
+ }
+ const user: User = transaction.user;
+ const tenant: Tenant = await TenantStorage.getTenant(tenantID);
+ let action: ServerAction;
+ switch (transactionAction) {
+ case TransactionAction.START:
+ action = ServerAction.START_TRANSACTION;
+ break;
+ case TransactionAction.UPDATE:
+ action = ServerAction.UPDATE_TRANSACTION;
+ break;
+ case TransactionAction.STOP:
+ action = ServerAction.STOP_TRANSACTION;
+ break;
+ }
+ if (!Utils.isTenantComponentActive(tenant, TenantComponents.OCPI)) {
+ throw new BackendError({
+ user: user,
+ action: action,
+ module: MODULE_NAME,
+ method: 'processOCPITransaction',
+ message: `Unable to ${transactionAction} transaction for user '${user.id}' not issued locally`
+ });
+ }
+ const ocpiClient = await OCPIClientFactory.getAvailableOcpiClient(tenant, OCPIRole.CPO) as CpoOCPIClient;
+ if (!ocpiClient) {
+ throw new BackendError({
+ user: user,
+ action: action,
+ module: MODULE_NAME,
+ method: 'processOCPITransaction',
+ message: `OCPI component requires at least one CPO endpoint to ${transactionAction} transactions`
+ });
+ }
+ let authorizationId;
+ let authorizations: DataResult<OCPPAuthorizeRequestExtended>;
+ switch (transactionAction) {
+ case TransactionAction.START:
+ // eslint-disable-next-line no-case-declarations
+ const tag = user.tags.find(((value) => value.id === transaction.tagID));
+ if (!tag.ocpiToken) {
+ throw new BackendError({
+ user: user,
+ action: action,
+ module: MODULE_NAME,
+ method: 'processOCPITransaction',
+ message: `User '${user.id}' with tag '${transaction.tagID}' cannot ${transactionAction} transaction thought OCPI protocol due to missing ocpiToken`
+ });
+ }
+ // Retrieve Authorization ID
+ authorizations = await OCPPStorage.getAuthorizes(tenant.id, {
+ dateFrom: moment(transaction.timestamp).subtract(10, 'minutes').toDate(),
+ chargeBoxID: transaction.chargeBoxID,
+ tagID: transaction.tagID
+ }, Constants.DB_PARAMS_MAX_LIMIT);
+ // Found ID?
+ if (authorizations && authorizations.result && authorizations.result.length > 0) {
+ // Get the first non used Authorization OCPI ID
+ for (const authorization of authorizations.result) {
+ if (authorization.authorizationId) {
+ const ocpiTransaction = await TransactionStorage.getOCPITransaction(tenant.id, authorization.authorizationId);
+ // OCPI ID not used yet
+ if (!ocpiTransaction) {
+ authorizationId = authorization.authorizationId;
+ break;
+ }
+ }
+ }
+ }
+ if (!authorizationId) {
+ throw new BackendError({
+ user: user,
+ action: action,
+ module: MODULE_NAME, method: 'processOCPITransaction',
+ message: `User '${user.id}' with tag '${transaction.tagID}' cannot ${transactionAction} transaction thought OCPI protocol due to missing Authorization`
+ });
+ }
+ await ocpiClient.startSession(tag.ocpiToken, chargingStation, transaction, authorizationId);
+ break;
+ case TransactionAction.UPDATE:
+ await ocpiClient.updateSession(transaction);
+ break;
+ case TransactionAction.STOP:
+ await ocpiClient.stopSession(transaction);
+ break;
+ case TransactionAction.END:
+ await ocpiClient.postCdr(transaction);
+ break;
+ }
+ }
+
public static async buildExtraConsumptionInactivity(tenantID: string, transaction: Transaction): Promise<boolean> {
// Extra inactivity
if (transaction.stop.extraInactivitySecs > 0) {

MODIFY
src/server/rest/CentralRestServerService.ts
src/server/rest/CentralRestServerService.ts
@@ -72,7 +72,8 @@ class RequestMapper {
[ServerAction.ADD_SITES_TO_USER]: UserService.handleAssignSitesToUser.bind(this),
[ServerAction.REMOVE_SITES_FROM_USER]: UserService.handleAssignSitesToUser.bind(this),
[ServerAction.SITE_AREA_CREATE]: SiteAreaService.handleCreateSiteArea.bind(this),
- [ServerAction.TRANSACTION_REFUND]: TransactionService.handleRefundTransactions.bind(this),
+ [ServerAction.TRANSACTIONS_REFUND]: TransactionService.handleRefundTransactions.bind(this),
+ [ServerAction.TRANSACTION_PUSH_CDR]: TransactionService.handlePushTransactionCdr.bind(this),
[ServerAction.SYNCHRONIZE_REFUNDED_TRANSACTIONS]: TransactionService.handleSynchronizeRefundedTransactions.bind(this),
[ServerAction.SETTING_CREATE]: SettingService.handleCreateSetting.bind(this),
[ServerAction.BILLING_SYNCHRONIZE_USERS]: BillingService.handleSynchronizeUsers.bind(this),

MODIFY
src/server/rest/service/TransactionService.ts
src/server/rest/service/TransactionService.ts
import { Action, Entity } from '../../../types/Authorization';
import { HTTPAuthError, HTTPError } from '../../../types/HTTPError';
import { NextFunction, Request, Response } from 'express';
+import Transaction, { TransactionAction } from '../../../types/Transaction';
import { ActionsResponse } from '../../../types/GlobalType';
import AppAuthError from '../../../exception/AppAuthError';
@@ -24,7 +25,6 @@ import { ServerAction } from '../../../types/Server';
import SynchronizeRefundTransactionsTask from '../../../scheduler/tasks/SynchronizeRefundTransactionsTask';
import TenantComponents from '../../../types/TenantComponents';
import TenantStorage from '../../../storage/mongodb/TenantStorage';
-import Transaction from '../../../types/Transaction';
import { TransactionInErrorType } from '../../../types/InError';
import TransactionSecurity from './security/TransactionSecurity';
import TransactionStorage from '../../../storage/mongodb/TransactionStorage';
@@ -150,6 +150,69 @@ export default class TransactionService {
next();
}
+ public static async handlePushTransactionCdr(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
+ // Filter
+ const filteredRequest = TransactionSecurity.filterPushTransactionCdrRequest(req.body);
+ // Check Mandatory fields
+ UtilsService.assertIdIsProvided(action, filteredRequest.transactionId, MODULE_NAME, 'handlePushTransactionCdr', req.user);
+ // Check auth
+ if (!Authorizations.canUpdateTransaction(req.user)) {
+ throw new AppAuthError({
+ errorCode: HTTPAuthError.ERROR,
+ user: req.user,
+ action: Action.UPDATE, entity: Entity.TRANSACTION,
+ module: MODULE_NAME, method: 'handlePushTransactionCdr',
+ value: filteredRequest.transactionId.toString()
+ });
+ }
+ // Check Transaction
+ const transaction = await TransactionStorage.getTransaction(req.user.tenantID, filteredRequest.transactionId);
+ UtilsService.assertObjectExists(action, transaction, `Transaction ID '${filteredRequest.transactionId}' does not exist`,
+ MODULE_NAME, 'handlePushTransactionCdr', req.user);
+ // Check Charging Station
+ const chargingStation = await ChargingStationStorage.getChargingStation(req.user.tenantID, transaction.chargeBoxID);
+ UtilsService.assertObjectExists(action, chargingStation, `Charging Station ID '${transaction.chargeBoxID}' does not exist`,
+ MODULE_NAME, 'handlePushTransactionCdr', req.user);
+ // Check Issuer
+ if (!transaction.issuer) {
+ throw new AppError({
+ source: Constants.CENTRAL_SERVER,
+ errorCode: HTTPError.TRANSACTION_NOT_FROM_TENANT,
+ message: `The transaction ID '${transaction.id}' belongs to an external organization`,
+ module: MODULE_NAME, method: 'handlePushTransactionCdr',
+ user: req.user,
+ action: action
+ });
+ }
+ // Check OCPI
+ if (!transaction.ocpiData) {
+ throw new AppError({
+ source: Constants.CENTRAL_SERVER,
+ errorCode: HTTPError.TRANSACTION_WITH_NO_OCPI_DATA,
+ message: `The transaction ID '${transaction.id}' has no OCPI data`,
+ module: MODULE_NAME, method: 'handlePushTransactionCdr',
+ user: req.user,
+ action: action
+ });
+ }
+ // CDR already pushed
+ if (transaction.ocpiData.cdr?.id) {
+ throw new AppError({
+ source: Constants.CENTRAL_SERVER,
+ errorCode: HTTPError.TRANSACTION_CDR_ALREADY_PUSHED,
+ message: `The CDR of the transaction ID '${transaction.id}' has already been pushed`,
+ module: MODULE_NAME, method: 'handlePushTransactionCdr',
+ user: req.user,
+ action: action
+ });
+ }
+ // Post CDR
+ await OCPPUtils.processOCPITransaction(req.user.tenantID, transaction, chargingStation, TransactionAction.END);
+ // Ok
+ res.json(Constants.REST_RESPONSE_SUCCESS);
+ next();
+ }
+
public static async handleGetUnassignedTransactionsCount(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
// Check Auth
if (!Authorizations.canUpdateTransaction(req.user)) {

MODIFY
src/server/rest/service/security/TransactionSecurity.ts
src/server/rest/service/security/TransactionSecurity.ts
-import { HttpAssignTransactionsToUserRequest, HttpConsumptionFromTransactionRequest, HttpTransactionRequest, HttpTransactionsRefundRequest, HttpTransactionsRequest, HttpUnassignTransactionsToUserRequest } from '../../../../types/requests/HttpTransactionRequest';
+import { HttpAssignTransactionsToUserRequest, HttpConsumptionFromTransactionRequest, HttpPushTransactionCdrRequest, HttpTransactionRequest, HttpTransactionsRefundRequest, HttpTransactionsRequest, HttpUnassignTransactionsToUserRequest } from '../../../../types/requests/HttpTransactionRequest';
import Transaction, { TransactionConsumption } from '../../../../types/Transaction';
import Authorizations from '../../../../authorization/Authorizations';
import Constants from '../../../../utils/Constants';
import Consumption from '../../../../types/Consumption';
import { DataResult } from '../../../../types/DataResult';
+import { OCPICdr } from '../../../../types/ocpi/OCPICdr';
import RefundReport from '../../../../types/Refund';
import { TransactionInError } from '../../../../types/InError';
import UserSecurity from './UserSecurity';
@@ -47,6 +48,12 @@ export default class TransactionSecurity {
};
}
+ public static filterPushTransactionCdrRequest(request: any): HttpPushTransactionCdrRequest {
+ return {
+ transactionId: Utils.convertToInt(sanitize(request.transactionId))
+ };
+ }
+
public static filterTransactionsActiveRequest(request: any): HttpTransactionsRequest {
const filteredRequest: HttpTransactionsRequest = {} as HttpTransactionsRequest;
if (request.Issuer) {
@@ -170,11 +177,14 @@ export default class TransactionSecurity {
filteredTransaction.refundData = transaction.refundData;
if (transaction.ocpiData) {
filteredTransaction.ocpiData = {
- session: transaction.ocpiData.session,
sessionCheckedOn: transaction.ocpiData.sessionCheckedOn,
- cdr: transaction.ocpiData.cdr,
- cdrCheckedOn: transaction.ocpiData.cdrCheckedOn
+ cdrCheckedOn: transaction.ocpiData.cdrCheckedOn,
};
+ if (transaction.ocpiData.cdr) {
+ filteredTransaction.ocpiData.cdr = {
+ id: transaction.ocpiData.cdr.id,
+ } as OCPICdr;
+ }
}
// Demo user?
if (Authorizations.isDemo(loggedUser)) {

MODIFY
src/types/HTTPError.ts
src/types/HTTPError.ts
@@ -45,6 +45,10 @@ export enum HTTPError {
USER_ALREADY_ASSIGNED_TO_CAR = 594,
NO_CAR_FOR_USER = 595,
+ TRANSACTION_NOT_FROM_TENANT = 580,
+ TRANSACTION_WITH_NO_OCPI_DATA = 581,
+ TRANSACTION_CDR_ALREADY_PUSHED = 582,
+
THREE_PHASE_CHARGER_ON_SINGLE_PHASE_SITE_AREA = 539,
}

MODIFY
src/types/Server.ts
src/types/Server.ts
@@ -306,7 +306,8 @@ export enum ServerAction {
SITE_AREA_UPDATE = 'SiteAreaUpdate',
SITE_AREA_DELETE = 'SiteAreaDelete',
- TRANSACTION_REFUND = 'TransactionsRefund',
+ TRANSACTIONS_REFUND = 'TransactionsRefund',
+ TRANSACTION_PUSH_CDR = 'TransactionPushCdr',
SYNCHRONIZE_REFUNDED_TRANSACTIONS = 'SynchronizeRefundedTransactions',
SETTING_CREATE = 'SettingCreate',

MODIFY
src/types/requests/HttpTransactionRequest.ts
src/types/requests/HttpTransactionRequest.ts
@@ -16,6 +16,10 @@ export interface HttpTransactionRequest {
ID: number;
}
+export interface HttpPushTransactionCdrRequest {
+ transactionId: number;
+}
+
export interface HttpTransactionsRequest extends HttpDatabaseRequest {
ChargeBoxID: string;
Issuer: boolean;

REST endpoint to push the CDR