Added Tag Visual ID also in Stop Transaction
Handle Tag ID in Transaction Service endpoint
Updated OAS file (Swagger)
Optimized Transaction with Visual ID / Removed from transaction storage
MODIFY
src/assets/server/rest/v1/docs/e-mobility-oas.json
src/assets/server/rest/v1/docs/e-mobility-oas.json
{
"$ref": "#/components/parameters/UserIDs"
},
+ {
+ "$ref": "#/components/parameters/WithTag"
+ },
{
"$ref": "#/components/parameters/TagIDs"
},
}
},
{
- "in": "query",
- "name": "TagID",
- "description": "Pipe separated Tag IDs",
- "required": false,
- "schema": {
- "type": "string"
- }
+ "$ref": "#/components/parameters/TagIDs"
+ },
+ {
+ "$ref": "#/components/parameters/VisualTagIDs"
},
{
"in": "query",
"type": "boolean"
}
},
+ "WithTag": {
+ "in": "query",
+ "name": "WithTag",
+ "description": "With tag 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"
},
+ "WithTag": {
+ "type": "boolean",
+ "sanitize": "mongo"
+ },
"ConnectorID": {
"$ref": "chargingstation.json#/definitions/connectorIDs"
},

MODIFY
src/server/rest/v1/service/TransactionService.ts
src/server/rest/v1/service/TransactionService.ts
@@ -14,7 +14,6 @@ import Configuration from '../../../../utils/Configuration';
import Constants from '../../../../utils/Constants';
import Consumption from '../../../../types/Consumption';
import ConsumptionStorage from '../../../../storage/mongodb/ConsumptionStorage';
-import Cypher from '../../../../utils/Cypher';
import { DataResult } from '../../../../types/DataResult';
import LockingHelper from '../../../../locking/LockingHelper';
import LockingManager from '../../../../locking/LockingManager';
@@ -47,10 +46,11 @@ export default class TransactionService {
res.json(await TransactionService.getTransactions(req, action, {}, [
'id', 'chargeBoxID', 'timestamp', 'issuer', 'stateOfCharge', 'timezone', 'connectorId', 'meterStart', 'siteAreaID', 'siteID', 'companyID',
'currentTotalDurationSecs', 'currentTotalInactivitySecs', 'currentInstantWatts', 'currentTotalConsumptionWh', 'currentStateOfCharge',
- 'currentCumulatedPrice', 'currentInactivityStatus', 'roundedPrice', 'price', 'priceUnit', 'tagID',
+ 'currentCumulatedPrice', 'currentInactivityStatus', 'roundedPrice', 'price', 'priceUnit',
'stop.roundedPrice', 'stop.price', 'stop.priceUnit', 'stop.inactivityStatus', 'stop.stateOfCharge', 'stop.timestamp', 'stop.totalConsumptionWh',
'stop.totalDurationSecs', 'stop.totalInactivitySecs', 'stop.extraInactivitySecs', 'stop.meterStop',
- 'billingData.stop.invoiceNumber', 'stop.reason', 'ocpi', 'ocpiWithCdr', 'tagID', 'stop.tagID', 'site.name', 'siteArea.name', 'company.name'
+ 'billingData.stop.invoiceNumber', 'stop.reason', 'ocpi', 'ocpiWithCdr', 'tagID', 'stop.tagID', 'tag.visualID', 'stop.tag.visualID',
+ 'site.name', 'siteArea.name', 'company.name'
]));
next();
}
@@ -547,12 +547,13 @@ export default class TransactionService {
if ((await Authorizations.canListUsers(req.user)).authorized) {
projectFields = [
...projectFields,
- 'userID', 'user.id', 'user.name', 'user.firstName', 'user.email',
+ 'userID', 'user.id', 'user.name', 'user.firstName', 'user.email', 'tagID', 'tag.visualID',
'stop.userID', 'stop.user.id', 'stop.user.name', 'stop.user.firstName', 'stop.user.email', 'stop.tagID', 'stop.tag.visualID'
];
}
// Get Transaction
- const transaction = await TransactionStorage.getTransaction(req.user.tenantID, filteredRequest.TransactionId, projectFields);
+ const transaction = await TransactionStorage.getTransaction(req.user.tenantID, filteredRequest.TransactionId,
+ { withTag: filteredRequest.WithTag }, projectFields);
UtilsService.assertObjectExists(action, transaction, `Transaction ID '${filteredRequest.TransactionId}' does not exist`,
MODULE_NAME, 'handleGetConsumptionFromTransaction', req.user);
// Check Transaction
@@ -623,15 +624,16 @@ export default class TransactionService {
const filteredRequest = TransactionSecurity.filterTransactionRequest(req.query);
UtilsService.assertIdIsProvided(action, filteredRequest.ID, MODULE_NAME, 'handleGetTransaction', req.user);
// Get Transaction
- const transaction = await TransactionStorage.getTransaction(req.user.tenantID, filteredRequest.ID, [
- 'id', 'chargeBoxID', 'timestamp', 'issuer', 'stateOfCharge', 'tagID', 'timezone', 'connectorId', 'meterStart', 'siteAreaID', 'siteID', 'companyID',
+ const transaction = await TransactionStorage.getTransaction(req.user.tenantID, filteredRequest.ID, { withTag: true }, [
+ 'id', 'chargeBoxID', 'timestamp', 'issuer', 'stateOfCharge', 'tagID', 'tag.visualID', 'tag.description', 'timezone', 'connectorId', 'meterStart', 'siteAreaID', 'siteID', 'companyID',
'userID', 'user.id', 'user.name', 'user.firstName', 'user.email', 'roundedPrice', 'price', 'priceUnit',
'stop.userID', 'stop.user.id', 'stop.user.name', 'stop.user.firstName', 'stop.user.email',
'currentTotalDurationSecs', 'currentTotalInactivitySecs', 'currentInstantWatts', 'currentTotalConsumptionWh', 'currentStateOfCharge',
'currentCumulatedPrice', 'currentInactivityStatus', 'signedData', 'stop.reason',
'stop.roundedPrice', 'stop.price', 'stop.priceUnit', 'stop.inactivityStatus', 'stop.stateOfCharge', 'stop.timestamp', 'stop.totalConsumptionWh', 'stop.meterStop',
- 'stop.totalDurationSecs', 'stop.totalInactivitySecs', 'stop.extraInactivitySecs', 'stop.pricingSource', 'stop.signedData', 'stop.tagID', 'tag.description',
- 'billingData.stop.status', 'billingData.stop.invoiceID', 'billingData.stop.invoiceStatus', 'billingData.stop.invoiceNumber',
+ 'stop.totalDurationSecs', 'stop.totalInactivitySecs', 'stop.extraInactivitySecs', 'stop.pricingSource', 'stop.signedData',
+ 'stop.tagID', 'stop.tag.visualID', 'stop.tag.description', 'billingData.stop.status', 'billingData.stop.invoiceID',
+ 'billingData.stop.invoiceStatus', 'billingData.stop.invoiceNumber',
]);
UtilsService.assertObjectExists(action, transaction, `Transaction ID '${filteredRequest.ID}' does not exist`,
MODULE_NAME, 'handleGetTransaction', req.user);
@@ -690,7 +692,7 @@ export default class TransactionService {
public static async handleGetTransactionsActive(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
req.query.Status = 'active';
- const transactions = await TransactionService.getTransactions(req, action, { withTag: true }, [
+ const transactions = await TransactionService.getTransactions(req, action, {}, [
'id', 'chargeBoxID', 'timestamp', 'issuer', 'stateOfCharge', 'timezone', 'connectorId', 'status', 'meterStart', 'siteAreaID', 'siteID', 'companyID',
'currentTotalDurationSecs', 'currentTotalInactivitySecs', 'currentInstantWatts', 'currentTotalConsumptionWh', 'currentStateOfCharge',
'currentCumulatedPrice', 'currentInactivityStatus', 'roundedPrice', 'price', 'priceUnit', 'tagID', 'tag.visualID', 'site.name', 'siteArea.name', 'company.name'
@@ -702,7 +704,7 @@ export default class TransactionService {
public static async handleGetTransactionsCompleted(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise<void> {
// Get transaction
req.query.Status = 'completed';
- const transactions = await TransactionService.getTransactions(req, action, { withTag: true }, [
+ const transactions = await TransactionService.getTransactions(req, action, {}, [
'id', 'chargeBoxID', 'timestamp', 'issuer', 'stateOfCharge', 'timezone', 'connectorId', 'meterStart', 'siteAreaID', 'siteID', 'companyID',
'stop.roundedPrice', 'stop.price', 'stop.priceUnit', 'stop.inactivityStatus', 'stop.stateOfCharge', 'stop.timestamp', 'stop.totalConsumptionWh',
'stop.totalDurationSecs', 'stop.totalInactivitySecs', 'stop.extraInactivitySecs', 'stop.meterStop', 'site.name', 'siteArea.name', 'company.name',
@@ -787,11 +789,12 @@ export default class TransactionService {
public static async getCompletedTransactionsToExport(req: Request): Promise<DataResult<Transaction>> {
// Get transaction
req.query.Status = 'completed';
- return TransactionService.getTransactions(req, ServerAction.TRANSACTIONS_EXPORT, { withTag: true }, [
+ req.query.WithTag = 'true';
+ return TransactionService.getTransactions(req, ServerAction.TRANSACTIONS_EXPORT, {}, [
'id', 'chargeBoxID', 'timestamp', 'issuer', 'stateOfCharge', 'timezone', 'connectorId', 'meterStart', 'siteAreaID', 'siteID', 'companyID',
'stop.roundedPrice', 'stop.price', 'stop.priceUnit', 'stop.inactivityStatus', 'stop.stateOfCharge', 'stop.timestamp', 'stop.totalConsumptionWh',
'stop.totalDurationSecs', 'stop.totalInactivitySecs', 'stop.extraInactivitySecs', 'site.name', 'siteArea.name', 'company.name',
- 'billingData.stop.invoiceNumber', 'stop.reason', 'ocpi', 'ocpiWithCdr', 'tagID', 'stop.tagID', 'tag.description'
+ 'billingData.stop.invoiceNumber', 'stop.reason', 'ocpi', 'ocpiWithCdr', 'tagID', 'stop.tagID', 'tag.description', 'stop.tag.description', 'tag.visualID', 'stop.tag.visualID'
]);
}
@@ -829,7 +832,7 @@ export default class TransactionService {
const filteredRequest = TransactionSecurity.filterTransactionRequest(req.query);
UtilsService.assertIdIsProvided(action, filteredRequest.ID, MODULE_NAME, 'handleExportTransactionOcpiCdr', req.user);
// Get Transaction
- const transaction = await TransactionStorage.getTransaction(req.user.tenantID, filteredRequest.ID, ['id', 'ocpiData']);
+ const transaction = await TransactionStorage.getTransaction(req.user.tenantID, filteredRequest.ID, {}, ['id', 'ocpiData']);
UtilsService.assertObjectExists(action, transaction, `Transaction ID '${filteredRequest.ID}' does not exist`,
MODULE_NAME, 'handleExportTransactionOcpiCdr', req.user);
// Check
@@ -925,6 +928,7 @@ export default class TransactionService {
'userID',
'user',
'tagID',
+ 'visualTagID',
'tagDescription',
'timezone',
'startDate',
@@ -948,9 +952,10 @@ export default class TransactionService {
transaction.company?.name,
transaction.site?.name,
transaction.siteArea?.name,
- transaction.user ? Cypher.hash(transaction.user.id) : '',
+ transaction.user ? transaction.user.id : '',
transaction.user ? Utils.buildUserFullName(transaction.user, false) : '',
transaction.tagID,
+ transaction.tag?.visualID,
transaction.tag?.description || '',
transaction.timezone || 'N/A (UTC by default)',
(transaction.timezone ? moment(transaction.timestamp).tz(transaction.timezone) : moment.utc(transaction.timestamp)).format('YYYY-MM-DD'),
@@ -1089,9 +1094,6 @@ export default class TransactionService {
const filteredRequest = TransactionValidator.getInstance().validateTransactionsGetReq(req.query);
// Build
const extrafilters: any = {};
- if (Utils.objectHasProperty(params, 'withTag')) {
- extrafilters.withTag = params.withTag;
- }
if (filteredRequest.Status === 'completed') {
extrafilters.stop = { $exists: true };
}
@@ -1103,6 +1105,13 @@ export default class TransactionService {
if (!Utils.isEmptyArray(httpProjectFields)) {
projectFields = projectFields.filter((projectField) => httpProjectFields.includes(projectField));
}
+ // Check Visual Tag 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)) {
+ filteredRequest.TagID = tagIDs.result.map((tag) => tag.id).join('|');
+ }
+ }
// Get the transactions
const transactions = await TransactionStorage.getTransactions(req.user.tenantID,
{
@@ -1110,9 +1119,9 @@ export default class TransactionService {
chargeBoxIDs: filteredRequest.ChargingStationID ? filteredRequest.ChargingStationID.split('|') : null,
issuer: Utils.objectHasProperty(filteredRequest, 'Issuer') ? filteredRequest.Issuer : null,
userIDs: filteredRequest.UserID ? filteredRequest.UserID.split('|') : null,
- visualTagIDs: filteredRequest.VisualTagID ? filteredRequest.VisualTagID.split('|') : null,
tagIDs: filteredRequest.TagID ? filteredRequest.TagID.split('|') : null,
ownerID: Authorizations.isBasic(req.user) ? req.user.id : null,
+ withTag: filteredRequest.WithTag,
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
@@ -108,6 +108,9 @@ export default class TransactionSecurity {
if (Utils.objectHasProperty(request, 'TransactionId')) {
filteredRequest.TransactionId = Utils.convertToInt(sanitize(request.TransactionId));
}
+ if (Utils.objectHasProperty(request, 'WithTag')) {
+ filteredRequest.WithTag = UtilsSecurity.filterBoolean(request.WithTag);
+ }
if (Utils.objectHasProperty(request, 'LoadAllConsumptions')) {
filteredRequest.LoadAllConsumptions = Utils.convertToBoolean(sanitize(request.LoadAllConsumptions));
}

MODIFY
src/storage/mongodb/TransactionStorage.ts
src/storage/mongodb/TransactionStorage.ts
@@ -272,7 +272,7 @@ export default class TransactionStorage {
params: {
transactionIDs?: number[]; issuer?: boolean; search?: string; ownerID?: string; userIDs?: string[]; siteAdminIDs?: string[];
chargeBoxIDs?: string[]; siteAreaIDs?: string[]; siteIDs?: string[]; connectorIDs?: number[]; startDateTime?: Date;
- endDateTime?: Date; stop?: any; minimalPrice?: boolean; reportIDs?: string[]; tagIDs?: string[]; visualTagIDs?: string[]; inactivityStatus?: string[];
+ 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;
@@ -357,14 +357,6 @@ export default class TransactionStorage {
if (params.tagIDs) {
filters.tagID = { $in: params.tagIDs };
}
- if (!Utils.isEmptyArray(params.visualTagIDs)) {
- DatabaseUtils.pushTagLookupInAggregation({
- tenantID, aggregation, localField: 'tagID', foreignField: '_id', asField: 'tag'
- });
- aggregation.push({
- $match: { 'tag.visualID': { $in: params.visualTagIDs } }
- });
- }
// Has user ID?
if (params.hasUserID) {
filters.$and = [
@@ -630,6 +622,10 @@ export default class TransactionStorage {
tenantID, aggregation: aggregation, asField: 'tag', localField: 'tagID',
foreignField: '_id', oneToOneCardinality: true
});
+ DatabaseUtils.pushTagLookupInAggregation({
+ tenantID, aggregation: aggregation, asField: 'stop.tag', localField: 'stop.tagID',
+ foreignField: '_id', oneToOneCardinality: true
+ });
}
// Charging Station
DatabaseUtils.pushChargingStationLookupInAggregation({
@@ -1018,10 +1014,10 @@ export default class TransactionStorage {
}
public static async getTransaction(tenantID: string, id: number = Constants.UNKNOWN_NUMBER_ID,
- projectFields?: string[]): Promise<Transaction> {
+ params?: { withTag?: boolean; }, projectFields?: string[]): Promise<Transaction> {
const transactionsMDB = await TransactionStorage.getTransactions(tenantID, {
transactionIDs: [id],
- withTag: true
+ withTag: params.withTag,
}, Constants.DB_PARAMS_SINGLE_RECORD, projectFields);
return transactionsMDB.count === 1 ? transactionsMDB.result[0] : null;
}

MODIFY
src/types/requests/HttpTransactionRequest.ts
src/types/requests/HttpTransactionRequest.ts
@@ -26,6 +26,7 @@ export interface HttpTransactionsRequest extends HttpDatabaseRequest {
ChargingStationID: string;
Issuer: boolean;
WithCompany: boolean;
+ WithTag: boolean;
WithSite: boolean;
WithSiteArea: boolean;
ConnectorID: string;
@@ -48,6 +49,7 @@ export interface HttpTransactionsRequest extends HttpDatabaseRequest {
export interface HttpConsumptionFromTransactionRequest {
TransactionId: number;
+ WithTag: boolean;
LoadAllConsumptions?: boolean;
StartDateTime: Date;
EndDateTime: Date;

Added Tag Visual ID also in Stop Transaction
Handle Tag ID in Transaction Service endpoint
Updated OAS file (Swagger)
Optimized Transaction with Visual ID / Removed from transaction storage