Handle WithUser, WithCar, WithChargingStation in Transaction endpoint (GDPR, perfs)
MODIFY
src/assets/server/rest/v1/docs/e-mobility-oas.json
src/assets/server/rest/v1/docs/e-mobility-oas.json
{
"$ref": "#/components/parameters/EndDateTime"
},
+ {
+ "$ref": "#/components/parameters/WithChargingStation"
+ },
{
"$ref": "#/components/parameters/ChargingStationIDs"
},
{
"$ref": "#/components/parameters/ConnectorIDs"
},
+ {
+ "$ref": "#/components/parameters/WithCar"
+ },
+ {
+ "$ref": "#/components/parameters/WithUser"
+ },
{
"$ref": "#/components/parameters/UserIDs"
},
"type": "boolean"
}
},
+ "WithUser": {
+ "in": "query",
+ "name": "WithUser",
+ "description": "With user attribute",
+ "schema": {
+ "type": "boolean"
+ }
+ },
+ "WithCar": {
+ "in": "query",
+ "name": "WithCar",
+ "description": "With car attribute",
+ "schema": {
+ "type": "boolean"
+ }
+ },
+ "WithChargingStation": {
+ "in": "query",
+ "name": "WithChargingStation",
+ "description": "With charging station attribute",
+ "schema": {
+ "type": "boolean"
+ }
+ },
"RefundStatus": {
"in": "query",
"name": "RefundStatus",

MODIFY
src/assets/server/rest/v1/schemas/transaction/transactions-get.json
src/assets/server/rest/v1/schemas/transaction/transactions-get.json
"type": "boolean",
"sanitize": "mongo"
},
+ "WithUser": {
+ "type": "boolean",
+ "sanitize": "mongo"
+ },
+ "WithCar": {
+ "type": "boolean",
+ "sanitize": "mongo"
+ },
+ "WithChargingStation": {
+ "type": "boolean",
+ "sanitize": "mongo"
+ },
"ConnectorID": {
"$ref": "chargingstation.json#/definitions/connectorIDs"
},

MODIFY
src/scheduler/tasks/SynchronizeRefundTransactionsTask.ts
src/scheduler/tasks/SynchronizeRefundTransactionsTask.ts
@@ -45,7 +45,7 @@ export default class SynchronizeRefundTransactionsTask extends SchedulerTask {
{ 'refundStatus': [RefundStatus.SUBMITTED] },
{ ...Constants.DB_PARAMS_MAX_LIMIT, sort: { 'userID': 1, 'refundData.reportId': 1 } });
// Check
- if (transactions.count > 0) {
+ if (!Utils.isEmptyArray(transactions.result)) {
// Process them
await Logging.logInfo({
tenantID: tenant.id,

MODIFY
src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/cpo/CPOCdrsEndpoint.ts
src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/cpo/CPOCdrsEndpoint.ts
@@ -50,7 +50,7 @@ export default class CPOCdrsEndpoint extends AbstractEndpoint {
// Get all transactions
const transactions = await TransactionStorage.getTransactions(tenant.id,
{ issuer: true, ocpiCdrDateFrom: dateFrom, ocpiCdrDateTo: dateTo, ocpiCdrChecked: true },
- { limit, skip }
+ { limit, skip }, ['ocpiData']
);
return {
count: transactions.count,

MODIFY
src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/cpo/CPOSessionsEndpoint.ts
src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/cpo/CPOSessionsEndpoint.ts
@@ -72,10 +72,9 @@ export default class CPOSessionsEndpoint extends AbstractEndpoint {
// Result
const sessions: OCPISession[] = [];
// Get all transactions
- const transactions = await TransactionStorage.getTransactions(tenant.id, { issuer: true, ocpiSessionDateFrom: dateFrom, ocpiSessionDateTo: dateTo }, {
- limit,
- skip
- });
+ const transactions = await TransactionStorage.getTransactions(tenant.id,
+ { issuer: true, ocpiSessionDateFrom: dateFrom, ocpiSessionDateTo: dateTo },
+ { limit, skip }, ['ocpiData']);
for (const transaction of transactions.result) {
sessions.push(transaction.ocpiData.session);
}

MODIFY
src/server/rest/v1/service/ChargingStationService.ts
src/server/rest/v1/service/ChargingStationService.ts
@@ -874,8 +874,8 @@ export default class ChargingStationService {
chargingStation.deleted = true;
// Check if charging station has had transactions
const transactions = await TransactionStorage.getTransactions(req.user.tenantID,
- { chargeBoxIDs: [chargingStation.id] }, Constants.DB_PARAMS_COUNT_ONLY);
- if (transactions.count > 0) {
+ { chargeBoxIDs: [chargingStation.id] }, Constants.DB_PARAMS_SINGLE_RECORD, ['id']);
+ if (!Utils.isEmptyArray(transactions.result)) {
// Delete logically
await ChargingStationStorage.saveChargingStation(req.tenant.id, chargingStation);
// Delete Charging Profiles

MODIFY
src/server/rest/v1/service/TagService.ts
src/server/rest/v1/service/TagService.ts
@@ -117,7 +117,7 @@ export default class TagService {
}
// Check if Tag has been already used
const transactions = await TransactionStorage.getTransactions(req.user.tenantID,
- { tagIDs: [filteredRequest.id.toUpperCase()], hasUserID: true }, Constants.DB_PARAMS_SINGLE_RECORD);
+ { tagIDs: [filteredRequest.id.toUpperCase()], hasUserID: true }, Constants.DB_PARAMS_SINGLE_RECORD, ['id']);
if (!Utils.isEmptyArray(transactions.result)) {
throw new AppError({
source: Constants.CENTRAL_SERVER,

MODIFY
src/server/rest/v1/service/TransactionService.ts
src/server/rest/v1/service/TransactionService.ts
@@ -533,7 +533,7 @@ export default class TransactionService {
if (await Authorizations.canListCars(req.user)) {
projectFields = [
...projectFields,
- 'carCatalog.vehicleMake', 'carCatalog.vehicleModel',
+ 'carID' ,'carCatalogID', 'carCatalog.vehicleMake', 'carCatalog.vehicleModel',
'carCatalog.vehicleModelVersion', 'carCatalog.image',
];
}
@@ -553,7 +553,7 @@ export default class TransactionService {
}
// Get Transaction
const transaction = await TransactionStorage.getTransaction(req.user.tenantID, filteredRequest.TransactionId,
- { withTag: filteredRequest.WithTag }, projectFields);
+ { withTag: filteredRequest.WithTag, withCar: filteredRequest.WithCar }, projectFields);
UtilsService.assertObjectExists(action, transaction, `Transaction ID '${filteredRequest.TransactionId}' does not exist`,
MODULE_NAME, 'handleGetConsumptionFromTransaction', req.user);
// Check Transaction
@@ -881,7 +881,7 @@ export default class TransactionService {
if (await Authorizations.canListCars(req.user)) {
projectFields = [
...projectFields,
- 'carCatalog.vehicleMake', 'carCatalog.vehicleModel', 'carCatalog.vehicleModelVersion',
+ 'carID' ,'carCatalogID', 'carCatalog.vehicleMake', 'carCatalog.vehicleModel', 'carCatalog.vehicleModelVersion',
];
}
}
@@ -1080,7 +1080,7 @@ export default class TransactionService {
if (await Authorizations.canListCars(req.user)) {
projectFields = [
...projectFields,
- 'carCatalog.vehicleMake', 'carCatalog.vehicleModel', 'carCatalog.vehicleModelVersion',
+ 'carID' ,'carCatalogID', 'carCatalog.vehicleMake', 'carCatalog.vehicleModel', 'carCatalog.vehicleModelVersion',
];
}
if (await Authorizations.canUpdateCar(req.user)) {
@@ -1105,7 +1105,7 @@ export default class TransactionService {
if (!Utils.isEmptyArray(httpProjectFields)) {
projectFields = projectFields.filter((projectField) => httpProjectFields.includes(projectField));
}
- // Check Visual Tag IDs
+ // Get Tag IDs from Visual IDs
if (filteredRequest.VisualTagID) {
const tagIDs = await TagStorage.getTags(req.tenant.id, { visualIDs: filteredRequest.VisualTagID.split('|') }, Constants.DB_PARAMS_MAX_LIMIT, ['id']);
if (!Utils.isEmptyArray(tagIDs.result)) {
@@ -1122,6 +1122,9 @@ export default class TransactionService {
tagIDs: filteredRequest.TagID ? filteredRequest.TagID.split('|') : null,
ownerID: Authorizations.isBasic(req.user) ? req.user.id : null,
withTag: filteredRequest.WithTag,
+ withUser: filteredRequest.WithUser,
+ withChargingStation: filteredRequest.WithChargingStation,
+ withCar: filteredRequest.WithCar,
withSite: filteredRequest.WithSite,
withCompany: filteredRequest.WithCompany,
siteAreaIDs: filteredRequest.SiteAreaID ? filteredRequest.SiteAreaID.split('|') : null,

MODIFY
src/server/rest/v1/service/security/TransactionSecurity.ts
src/server/rest/v1/service/security/TransactionSecurity.ts
@@ -111,6 +111,9 @@ export default class TransactionSecurity {
if (Utils.objectHasProperty(request, 'WithTag')) {
filteredRequest.WithTag = UtilsSecurity.filterBoolean(request.WithTag);
}
+ if (Utils.objectHasProperty(request, 'WithCar')) {
+ filteredRequest.WithCar = UtilsSecurity.filterBoolean(request.WithCar);
+ }
if (Utils.objectHasProperty(request, 'LoadAllConsumptions')) {
filteredRequest.LoadAllConsumptions = Utils.convertToBoolean(sanitize(request.LoadAllConsumptions));
}

MODIFY
src/storage/mongodb/TransactionStorage.ts
src/storage/mongodb/TransactionStorage.ts
@@ -271,11 +271,11 @@ export default class TransactionStorage {
public static async getTransactions(tenantID: string,
params: {
transactionIDs?: number[]; issuer?: boolean; search?: string; ownerID?: string; userIDs?: string[]; siteAdminIDs?: string[];
- chargeBoxIDs?: string[]; siteAreaIDs?: string[]; siteIDs?: string[]; connectorIDs?: number[]; startDateTime?: Date;
+ chargeBoxIDs?: string[]; siteAreaIDs?: string[]; siteIDs?: string[]; connectorIDs?: number[]; startDateTime?: Date; withChargingStation?: boolean;
endDateTime?: Date; stop?: any; minimalPrice?: boolean; reportIDs?: string[]; tagIDs?: string[]; inactivityStatus?: string[];
ocpiSessionID?: string; ocpiAuthorizationID?: string; ocpiSessionDateFrom?: Date; ocpiSessionDateTo?: Date; ocpiCdrDateFrom?: Date; ocpiCdrDateTo?: Date;
ocpiSessionChecked?: boolean; ocpiCdrChecked?: boolean; oicpSessionID?: string; withSite?: boolean; withSiteArea?: boolean; withCompany?: boolean;
- statistics?: 'refund' | 'history' | 'ongoing'; refundStatus?: string[]; withTag?: boolean; hasUserID?: boolean;
+ statistics?: 'refund' | 'history' | 'ongoing'; refundStatus?: string[]; withTag?: boolean; hasUserID?: boolean; withUser?: boolean; withCar?: boolean;
},
dbParams: DbParams, projectFields?: string[]):
Promise<{
@@ -627,11 +627,6 @@ export default class TransactionStorage {
foreignField: '_id', oneToOneCardinality: true
});
}
- // Charging Station
- DatabaseUtils.pushChargingStationLookupInAggregation({
- tenantID, aggregation: aggregation, localField: 'chargeBoxID', foreignField: '_id',
- asField: 'chargeBox', oneToOneCardinality: true, oneToOneCardinalityNotNull: false
- });
// Company
if (params.withCompany) {
DatabaseUtils.pushCompanyLookupInAggregation({
@@ -653,6 +648,12 @@ export default class TransactionStorage {
asField: 'siteArea', oneToOneCardinality: true
});
}
+ // Charging Station
+ if (params.withChargingStation) {
+ DatabaseUtils.pushChargingStationLookupInAggregation({
+ tenantID, aggregation: aggregation, localField: 'chargeBoxID', foreignField: '_id',
+ asField: 'chargeBox', oneToOneCardinality: true, oneToOneCardinalityNotNull: false
+ });
DatabaseUtils.pushConvertObjectIDToString(aggregation, 'chargeBox.siteAreaID');
// Add Connector and Status
if (projectFields && projectFields.includes('status')) {
@@ -670,26 +671,29 @@ export default class TransactionStorage {
$addFields: { status: '$connector.status' }
});
}
- // Users
+ }
+ // User
+ if (params.withUser) {
DatabaseUtils.pushUserLookupInAggregation({
tenantID, aggregation: aggregation, asField: 'user', localField: 'userID',
foreignField: '_id', oneToOneCardinality: true, oneToOneCardinalityNotNull: false
});
+ DatabaseUtils.pushUserLookupInAggregation({
+ tenantID, aggregation: aggregation, asField: 'stop.user', localField: 'stop.userID',
+ foreignField: '_id', oneToOneCardinality: true, oneToOneCardinalityNotNull: false
+ });
+ }
// Car
+ if (params.withCar) {
DatabaseUtils.pushCarLookupInAggregation({
tenantID, aggregation: aggregation, asField: 'car', localField: 'carID',
foreignField: '_id', oneToOneCardinality: true, oneToOneCardinalityNotNull: false
});
- // Car Catalog
DatabaseUtils.pushCarCatalogLookupInAggregation({
tenantID: Constants.DEFAULT_TENANT, aggregation: aggregation, asField: 'carCatalog', localField: 'carCatalogID',
foreignField: '_id', oneToOneCardinality: true
});
- // User
- DatabaseUtils.pushUserLookupInAggregation({
- tenantID, aggregation: aggregation, asField: 'stop.user', localField: 'stop.userID',
- foreignField: '_id', oneToOneCardinality: true, oneToOneCardinalityNotNull: false
- });
+ }
// Rename ID
DatabaseUtils.pushRenameDatabaseIDToNumber(aggregation);
// Convert Object ID to string
@@ -1014,29 +1018,37 @@ export default class TransactionStorage {
}
public static async getTransaction(tenantID: string, id: number = Constants.UNKNOWN_NUMBER_ID,
- params?: { withTag?: boolean; }, projectFields?: string[]): Promise<Transaction> {
+ params: { withTag?: boolean; withCar?: boolean; } = {}, projectFields?: string[]): Promise<Transaction> {
const transactionsMDB = await TransactionStorage.getTransactions(tenantID, {
transactionIDs: [id],
withTag: params.withTag,
+ withCar: params.withCar,
+ withUser: true,
}, Constants.DB_PARAMS_SINGLE_RECORD, projectFields);
return transactionsMDB.count === 1 ? transactionsMDB.result[0] : null;
}
public static async getOCPITransactionBySessionID(tenantID: string, sessionID: string): Promise<Transaction> {
const transactionsMDB = await TransactionStorage.getTransactions(tenantID,
- { ocpiSessionID: sessionID }, Constants.DB_PARAMS_SINGLE_RECORD);
+ {
+ ocpiSessionID: sessionID
+ }, Constants.DB_PARAMS_SINGLE_RECORD);
return transactionsMDB.count === 1 ? transactionsMDB.result[0] : null;
}
public static async getOCPITransactionByAuthorizationID(tenantID: string, authorizationID: string): Promise<Transaction> {
const transactionsMDB = await TransactionStorage.getTransactions(tenantID,
- { ocpiAuthorizationID: authorizationID }, Constants.DB_PARAMS_SINGLE_RECORD);
+ {
+ ocpiAuthorizationID: authorizationID
+ }, Constants.DB_PARAMS_SINGLE_RECORD);
return transactionsMDB.count === 1 ? transactionsMDB.result[0] : null;
}
public static async getOICPTransactionBySessionID(tenantID: string, oicpSessionID: string): Promise<Transaction> {
const transactionsMDB = await TransactionStorage.getTransactions(tenantID,
- { oicpSessionID: oicpSessionID }, Constants.DB_PARAMS_SINGLE_RECORD);
+ {
+ oicpSessionID: oicpSessionID
+ }, Constants.DB_PARAMS_SINGLE_RECORD);
return transactionsMDB.count === 1 ? transactionsMDB.result[0] : null;
}

MODIFY
src/types/requests/HttpTransactionRequest.ts
src/types/requests/HttpTransactionRequest.ts
@@ -27,6 +27,9 @@ export interface HttpTransactionsRequest extends HttpDatabaseRequest {
Issuer: boolean;
WithCompany: boolean;
WithTag: boolean;
+ WithUser: boolean;
+ WithChargingStation: boolean;
+ WithCar: boolean;
WithSite: boolean;
WithSiteArea: boolean;
ConnectorID: string;
@@ -50,6 +53,7 @@ export interface HttpTransactionsRequest extends HttpDatabaseRequest {
export interface HttpConsumptionFromTransactionRequest {
TransactionId: number;
WithTag: boolean;
+ WithCar: boolean;
LoadAllConsumptions?: boolean;
StartDateTime: Date;
EndDateTime: Date;

Handle WithUser, WithCar, WithChargingStation in Transaction endpoint (GDPR, perfs)