diff --git a/package-lock.json b/package-lock.json index 2ca6577d6c..8b5e33e9b9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "ev-server", - "version": "2.4.73", + "version": "2.4.74", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 521dfbf3e6..33b5294c45 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ev-server", - "version": "2.4.73", + "version": "2.4.74", "engines": { "node": "14.x.x", "npm": "6.x.x" diff --git a/src/assets/charging-station-templates b/src/assets/charging-station-templates index 5a32fd9297..e127d51334 160000 --- a/src/assets/charging-station-templates +++ b/src/assets/charging-station-templates @@ -1 +1 @@ -Subproject commit 5a32fd9297e9ab6d12db0d06b20e31c8c8d741d5 +Subproject commit e127d51334469495aa8832bd8a7c356fcb83f1b7 diff --git a/src/assets/server/rest/v1/docs/e-mobility-oas.json b/src/assets/server/rest/v1/docs/e-mobility-oas.json index 61aaa660ee..e14bb03426 100644 --- a/src/assets/server/rest/v1/docs/e-mobility-oas.json +++ b/src/assets/server/rest/v1/docs/e-mobility-oas.json @@ -723,9 +723,18 @@ { "$ref": "#/components/parameters/SiteIDs" }, + { + "$ref": "#/components/parameters/WithSite" + }, { "$ref": "#/components/parameters/SiteAreaIDs" }, + { + "$ref": "#/components/parameters/WithSiteArea" + }, + { + "$ref": "#/components/parameters/WithCompany" + }, { "$ref": "#/components/parameters/InactivityStatuses" }, @@ -1066,6 +1075,9 @@ { "$ref": "#/components/parameters/SiteAreaIDs" }, + { + "$ref": "#/components/parameters/WithSiteArea" + }, { "$ref": "#/components/parameters/ConnectorStatuses" }, @@ -6459,6 +6471,14 @@ "type": "boolean" } }, + "WithCompany": { + "in": "query", + "name": "WithCompany", + "description": "With company attribute", + "schema": { + "type": "boolean" + } + }, "WithSite": { "in": "query", "name": "WithSite", @@ -6467,6 +6487,14 @@ "type": "boolean" } }, + "WithSiteArea": { + "in": "query", + "name": "WithSiteArea", + "description": "With site area attribute", + "schema": { + "type": "boolean" + } + }, "RefundStatus": { "in": "query", "name": "RefundStatus", diff --git a/src/assets/server/rest/v1/schemas/chargingstation/chargingstations-get.json b/src/assets/server/rest/v1/schemas/chargingstation/chargingstations-get.json index 900fa3f3e8..b55f83ab25 100644 --- a/src/assets/server/rest/v1/schemas/chargingstation/chargingstations-get.json +++ b/src/assets/server/rest/v1/schemas/chargingstation/chargingstations-get.json @@ -16,10 +16,17 @@ "SiteID": { "$ref": "common.json#/definitions/ids" }, + "CompanyID": { + "$ref": "common.json#/definitions/ids" + }, "WithSite": { "type": "boolean", "sanitize": "mongo" }, + "WithSiteArea": { + "type": "boolean", + "sanitize": "mongo" + }, "SiteAreaID": { "$ref": "common.json#/definitions/ids" }, diff --git a/src/assets/server/rest/v1/schemas/tag/tags-get.json b/src/assets/server/rest/v1/schemas/tag/tags-get.json index 16f7303000..2802ccef22 100644 --- a/src/assets/server/rest/v1/schemas/tag/tags-get.json +++ b/src/assets/server/rest/v1/schemas/tag/tags-get.json @@ -12,6 +12,10 @@ "UserID": { "$ref": "user.json#/definitions/ids" }, + "WithUser": { + "type": "boolean", + "sanitize": "mongo" + }, "Search": { "$ref": "common.json#/definitions/search" }, diff --git a/src/assets/server/rest/v1/schemas/transaction/transactions-get.json b/src/assets/server/rest/v1/schemas/transaction/transactions-get.json index fc590b9c12..ac260e1412 100644 --- a/src/assets/server/rest/v1/schemas/transaction/transactions-get.json +++ b/src/assets/server/rest/v1/schemas/transaction/transactions-get.json @@ -9,6 +9,18 @@ "type": "boolean", "sanitize": "mongo" }, + "WithCompany": { + "type": "boolean", + "sanitize": "mongo" + }, + "WithSite": { + "type": "boolean", + "sanitize": "mongo" + }, + "WithSiteArea": { + "type": "boolean", + "sanitize": "mongo" + }, "ConnectorID": { "$ref": "chargingstation.json#/definitions/connectorIDs" }, diff --git a/src/async-task/tasks/ocpi/OCPICheckCdrsAsyncTask.ts b/src/async-task/tasks/ocpi/OCPICheckCdrsAsyncTask.ts index 4bbff9a2e0..fd19e0af42 100644 --- a/src/async-task/tasks/ocpi/OCPICheckCdrsAsyncTask.ts +++ b/src/async-task/tasks/ocpi/OCPICheckCdrsAsyncTask.ts @@ -16,7 +16,7 @@ export default class OCPICheckCdrsAsyncTask extends AbstractAsyncTask { if (Utils.isTenantComponentActive(tenant, TenantComponents.OCPI)) { try { // Get the OCPI Endpoint - const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpoint(tenant.id, this.asyncTask.parameters.endpointID); + const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpoint(tenant, this.asyncTask.parameters.endpointID); if (!ocpiEndpoint) { throw new Error(`Unknown OCPI Endpoint ID '${this.asyncTask.parameters.endpointID}'`); } diff --git a/src/async-task/tasks/ocpi/OCPICheckLocationsAsyncTask.ts b/src/async-task/tasks/ocpi/OCPICheckLocationsAsyncTask.ts index fb60a82da7..6eace8a601 100644 --- a/src/async-task/tasks/ocpi/OCPICheckLocationsAsyncTask.ts +++ b/src/async-task/tasks/ocpi/OCPICheckLocationsAsyncTask.ts @@ -16,7 +16,7 @@ export default class OCPICheckLocationsAsyncTask extends AbstractAsyncTask { if (Utils.isTenantComponentActive(tenant, TenantComponents.OCPI)) { try { // Get the OCPI Endpoint - const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpoint(tenant.id, this.asyncTask.parameters.endpointID); + const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpoint(tenant, this.asyncTask.parameters.endpointID); if (!ocpiEndpoint) { throw new Error(`Unknown OCPI Endpoint ID '${this.asyncTask.parameters.endpointID}'`); } diff --git a/src/async-task/tasks/ocpi/OCPICheckSessionsAsyncTask.ts b/src/async-task/tasks/ocpi/OCPICheckSessionsAsyncTask.ts index a2eaeeda3b..de42bbfb3e 100644 --- a/src/async-task/tasks/ocpi/OCPICheckSessionsAsyncTask.ts +++ b/src/async-task/tasks/ocpi/OCPICheckSessionsAsyncTask.ts @@ -16,7 +16,7 @@ export default class OCPICheckSessionsAsyncTask extends AbstractAsyncTask { if (Utils.isTenantComponentActive(tenant, TenantComponents.OCPI)) { try { // Get the OCPI Endpoint - const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpoint(tenant.id, this.asyncTask.parameters.endpointID); + const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpoint(tenant, this.asyncTask.parameters.endpointID); if (!ocpiEndpoint) { throw new Error(`Unknown OCPI Endpoint ID '${this.asyncTask.parameters.endpointID}'`); } diff --git a/src/async-task/tasks/ocpi/OCPIPullCdrsAsyncTask.ts b/src/async-task/tasks/ocpi/OCPIPullCdrsAsyncTask.ts index 44d39aa16a..87a8e985f5 100644 --- a/src/async-task/tasks/ocpi/OCPIPullCdrsAsyncTask.ts +++ b/src/async-task/tasks/ocpi/OCPIPullCdrsAsyncTask.ts @@ -16,7 +16,7 @@ export default class OCPIPullCdrsAsyncTask extends AbstractAsyncTask { if (Utils.isTenantComponentActive(tenant, TenantComponents.OCPI)) { try { // Get the OCPI Endpoint - const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpoint(tenant.id, this.asyncTask.parameters.endpointID); + const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpoint(tenant, this.asyncTask.parameters.endpointID); if (!ocpiEndpoint) { throw new Error(`Unknown OCPI Endpoint ID '${this.asyncTask.parameters.endpointID}'`); } diff --git a/src/async-task/tasks/ocpi/OCPIPullLocationsAsyncTask.ts b/src/async-task/tasks/ocpi/OCPIPullLocationsAsyncTask.ts index 3a58ef4c21..8aac28deb5 100644 --- a/src/async-task/tasks/ocpi/OCPIPullLocationsAsyncTask.ts +++ b/src/async-task/tasks/ocpi/OCPIPullLocationsAsyncTask.ts @@ -16,7 +16,7 @@ export default class OCPIPullLocationsAsyncTask extends AbstractAsyncTask { if (Utils.isTenantComponentActive(tenant, TenantComponents.OCPI)) { try { // Get the OCPI Endpoint - const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpoint(tenant.id, this.asyncTask.parameters.endpointID); + const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpoint(tenant, this.asyncTask.parameters.endpointID); if (!ocpiEndpoint) { throw new Error(`Unknown OCPI Endpoint ID '${this.asyncTask.parameters.endpointID}'`); } diff --git a/src/async-task/tasks/ocpi/OCPIPullSessionsAsyncTask.ts b/src/async-task/tasks/ocpi/OCPIPullSessionsAsyncTask.ts index 89898bdcef..fd85345d9a 100644 --- a/src/async-task/tasks/ocpi/OCPIPullSessionsAsyncTask.ts +++ b/src/async-task/tasks/ocpi/OCPIPullSessionsAsyncTask.ts @@ -16,7 +16,7 @@ export default class OCPIPullSessionsAsyncTask extends AbstractAsyncTask { if (Utils.isTenantComponentActive(tenant, TenantComponents.OCPI)) { try { // Get the OCPI Endpoint - const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpoint(tenant.id, this.asyncTask.parameters.endpointID); + const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpoint(tenant, this.asyncTask.parameters.endpointID); if (!ocpiEndpoint) { throw new Error(`Unknown OCPI Endpoint ID '${this.asyncTask.parameters.endpointID}'`); } diff --git a/src/async-task/tasks/ocpi/OCPIPullTokensAsyncTask.ts b/src/async-task/tasks/ocpi/OCPIPullTokensAsyncTask.ts index 3413f931af..31394b5bd7 100644 --- a/src/async-task/tasks/ocpi/OCPIPullTokensAsyncTask.ts +++ b/src/async-task/tasks/ocpi/OCPIPullTokensAsyncTask.ts @@ -16,7 +16,7 @@ export default class OCPIPullTokensAsyncTask extends AbstractAsyncTask { if (Utils.isTenantComponentActive(tenant, TenantComponents.OCPI)) { try { // Get the OCPI Endpoint - const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpoint(tenant.id, this.asyncTask.parameters.endpointID); + const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpoint(tenant, this.asyncTask.parameters.endpointID); if (!ocpiEndpoint) { throw new Error(`Unknown OCPI Endpoint ID '${this.asyncTask.parameters.endpointID}'`); } diff --git a/src/async-task/tasks/ocpi/OCPIPushEVSEStatusesAsyncTask.ts b/src/async-task/tasks/ocpi/OCPIPushEVSEStatusesAsyncTask.ts index a8c079462c..97f9d4d15f 100644 --- a/src/async-task/tasks/ocpi/OCPIPushEVSEStatusesAsyncTask.ts +++ b/src/async-task/tasks/ocpi/OCPIPushEVSEStatusesAsyncTask.ts @@ -16,7 +16,7 @@ export default class OCPIPushEVSEStatusesAsyncTask extends AbstractAsyncTask { if (Utils.isTenantComponentActive(tenant, TenantComponents.OCPI)) { try { // Get the OCPI Endpoint - const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpoint(tenant.id, this.asyncTask.parameters.endpointID); + const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpoint(tenant, this.asyncTask.parameters.endpointID); if (!ocpiEndpoint) { throw new Error(`Unknown OCPI Endpoint ID '${this.asyncTask.parameters.endpointID}'`); } diff --git a/src/async-task/tasks/ocpi/OCPIPushTokensAsyncTask.ts b/src/async-task/tasks/ocpi/OCPIPushTokensAsyncTask.ts index baac8afb89..5576851ce9 100644 --- a/src/async-task/tasks/ocpi/OCPIPushTokensAsyncTask.ts +++ b/src/async-task/tasks/ocpi/OCPIPushTokensAsyncTask.ts @@ -16,7 +16,7 @@ export default class OCPIPushTokensAsyncTask extends AbstractAsyncTask { if (Utils.isTenantComponentActive(tenant, TenantComponents.OCPI)) { try { // Get the OCPI Endpoint - const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpoint(tenant.id, this.asyncTask.parameters.endpointID); + const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpoint(tenant, this.asyncTask.parameters.endpointID); if (!ocpiEndpoint) { throw new Error(`Unknown OCPI Endpoint ID '${this.asyncTask.parameters.endpointID}'`); } diff --git a/src/authorization/Authorizations.ts b/src/authorization/Authorizations.ts index 152a3fa6f1..d72a2892da 100644 --- a/src/authorization/Authorizations.ts +++ b/src/authorization/Authorizations.ts @@ -54,11 +54,11 @@ export default class Authorizations { public static async canStartTransaction(loggedUser: UserToken, chargingStation: ChargingStation): Promise { let context: AuthorizationContext; if (Utils.isComponentActiveFromToken(loggedUser, TenantComponents.ORGANIZATION)) { - if (!chargingStation || !chargingStation.siteArea || !chargingStation.siteArea.site) { + if (!chargingStation || !chargingStation.siteArea || !chargingStation.site) { return false; } context = { - site: chargingStation.siteArea.site.id, + site: chargingStation.site.id, sites: loggedUser.sites, sitesAdmin: loggedUser.sitesAdmin }; @@ -269,7 +269,7 @@ export default class Authorizations { context = { tagIDs: loggedUser.tagIDs, owner: loggedUser.id, - site: isOrgCompActive && chargingStation.siteArea ? chargingStation.siteArea.site.id : null, + site: isOrgCompActive ? chargingStation.siteID : null, sites: loggedUser.sites, sitesAdmin: loggedUser.sitesAdmin }; @@ -1068,9 +1068,8 @@ export default class Authorizations { }); } // Site ----------------------------------------------------- - chargingStation.siteArea.site = chargingStation.siteArea.site ?? - (chargingStation.siteArea.siteID ? await SiteStorage.getSite(tenant, chargingStation.siteArea.siteID) : null); - if (!chargingStation.siteArea.site) { + chargingStation.site = await SiteStorage.getSite(tenant, chargingStation.siteID); + if (!chargingStation.site) { // Reject Site Not Found throw new BackendError({ source: chargingStation.id, diff --git a/src/authorization/AuthorizationsDefinition.ts b/src/authorization/AuthorizationsDefinition.ts index d01545ff74..866218bcce 100644 --- a/src/authorization/AuthorizationsDefinition.ts +++ b/src/authorization/AuthorizationsDefinition.ts @@ -181,7 +181,7 @@ const AUTHORIZATION_DEFINITION: AuthorizationDefinition = { attributes: [ 'id', 'inactive', 'public', 'chargingStationURL', 'issuer', 'maximumPower', 'excludeFromSmartCharging', 'lastReboot', 'siteAreaID', 'siteArea.id', 'siteArea.name', 'siteArea.smartCharging', 'siteArea.siteID', - 'siteArea.site.id', 'siteArea.site.name', 'siteID', 'voltage', 'coordinates', 'forceInactive', 'manualConfiguration', 'firmwareUpdateStatus', + 'site.id', 'site.name', 'siteID', 'voltage', 'coordinates', 'forceInactive', 'manualConfiguration', 'firmwareUpdateStatus', 'capabilities', 'endpoint', 'chargePointVendor', 'chargePointModel', 'ocppVersion', 'ocppProtocol', 'lastSeen', 'firmwareVersion', 'currentIPAddress', 'ocppStandardParameters', 'ocppVendorParameters', 'connectors', 'chargePoints', 'createdOn', 'chargeBoxSerialNumber', 'chargePointSerialNumber', 'powerLimitUnit' @@ -471,7 +471,7 @@ const AUTHORIZATION_DEFINITION: AuthorizationDefinition = { attributes: [ 'id', 'inactive', 'public', 'chargingStationURL', 'issuer', 'maximumPower', 'excludeFromSmartCharging', 'lastReboot', 'siteAreaID', 'siteArea.id', 'siteArea.name', 'siteArea.smartCharging', 'siteArea.siteID', - 'siteArea.site.id', 'siteArea.site.name', 'siteID', 'voltage', 'coordinates', 'forceInactive', 'manualConfiguration', 'firmwareUpdateStatus', + 'site.id', 'site.name', 'siteID', 'voltage', 'coordinates', 'forceInactive', 'manualConfiguration', 'firmwareUpdateStatus', 'capabilities', 'endpoint', 'chargePointVendor', 'chargePointModel', 'ocppVersion', 'ocppProtocol', 'lastSeen', 'firmwareVersion', 'currentIPAddress', 'ocppStandardParameters', 'ocppVendorParameters', 'connectors', 'chargePoints', 'createdOn', 'chargeBoxSerialNumber', 'chargePointSerialNumber', 'powerLimitUnit' @@ -661,7 +661,7 @@ const AUTHORIZATION_DEFINITION: AuthorizationDefinition = { attributes: [ 'id', 'inactive', 'public', 'chargingStationURL', 'issuer', 'maximumPower', 'excludeFromSmartCharging', 'lastReboot', 'siteAreaID', 'siteArea.id', 'siteArea.name', 'siteArea.smartCharging', 'siteArea.siteID', - 'siteArea.site.id', 'siteArea.site.name', 'siteID', 'voltage', 'coordinates', 'forceInactive', 'manualConfiguration', 'firmwareUpdateStatus', + 'site.id', 'site.name', 'siteID', 'voltage', 'coordinates', 'forceInactive', 'manualConfiguration', 'firmwareUpdateStatus', 'capabilities', 'endpoint', 'chargePointVendor', 'chargePointModel', 'ocppVersion', 'ocppProtocol', 'lastSeen', 'firmwareVersion', 'currentIPAddress', 'ocppStandardParameters', 'ocppVendorParameters', 'connectors', 'chargePoints', 'createdOn', 'chargeBoxSerialNumber', 'chargePointSerialNumber', 'powerLimitUnit' diff --git a/src/client/ocpi/CpoOCPIClient.ts b/src/client/ocpi/CpoOCPIClient.ts index 024fc73c8d..b4ca0ec4cd 100644 --- a/src/client/ocpi/CpoOCPIClient.ts +++ b/src/client/ocpi/CpoOCPIClient.ts @@ -744,7 +744,7 @@ export default class CpoOCPIClient extends OCPIClient { } // Save const executionDurationSecs = (new Date().getTime() - startTime) / 1000; - await OCPIEndpointStorage.saveOcpiEndpoint(this.tenant.id, this.ocpiEndpoint); + await OCPIEndpointStorage.saveOcpiEndpoint(this.tenant, this.ocpiEndpoint); await Logging.logOcpiResult(this.tenant.id, ServerAction.OCPI_PATCH_STATUS, MODULE_NAME, 'sendEVSEStatuses', result, `{{inSuccess}} EVSE Status(es) were successfully patched in ${executionDurationSecs}s`, diff --git a/src/client/ocpi/EmspOCPIClient.ts b/src/client/ocpi/EmspOCPIClient.ts index d0a36cc05f..f8cd988739 100644 --- a/src/client/ocpi/EmspOCPIClient.ts +++ b/src/client/ocpi/EmspOCPIClient.ts @@ -105,7 +105,7 @@ export default class EmspOCPIClient extends OCPIClient { }; } // Save - await OCPIEndpointStorage.saveOcpiEndpoint(this.tenant.id, this.ocpiEndpoint); + await OCPIEndpointStorage.saveOcpiEndpoint(this.tenant, this.ocpiEndpoint); const executionDurationSecs = (new Date().getTime() - startTime) / 1000; await Logging.logOcpiResult(this.tenant.id, ServerAction.OCPI_PUSH_TOKENS, MODULE_NAME, 'sendTokens', result, @@ -346,7 +346,7 @@ export default class EmspOCPIClient extends OCPIClient { const locationName = site.name + Constants.OCPI_SEPARATOR + location.id; // Handle Site Area const siteAreas = await SiteAreaStorage.getSiteAreas(this.tenant.id, - { siteIDs: [site.id], name: locationName, issuer: false }, + { siteIDs: [site.id], name: locationName, issuer: false, withSite: true }, Constants.DB_PARAMS_SINGLE_RECORD); let siteArea = !Utils.isEmptyArray(siteAreas.result) ? siteAreas.result[0] : null; if (!siteArea) { @@ -402,8 +402,9 @@ export default class EmspOCPIClient extends OCPIClient { } // Update Charging Station const chargingStation = OCPIUtilsService.convertEvseToChargingStation(evse, location); - chargingStation.siteAreaID = siteArea.id; + chargingStation.companyID = siteArea.site?.companyID; chargingStation.siteID = siteArea.siteID; + chargingStation.siteAreaID = siteArea.id; await ChargingStationStorage.saveChargingStation(this.tenant.id, chargingStation); await ChargingStationStorage.saveChargingStationOcpiData(this.tenant.id, chargingStation.id, chargingStation.ocpiData); await Logging.logDebug({ diff --git a/src/client/ocpi/OCPIClient.ts b/src/client/ocpi/OCPIClient.ts index 5caa06b5be..69ed15f735 100644 --- a/src/client/ocpi/OCPIClient.ts +++ b/src/client/ocpi/OCPIClient.ts @@ -90,7 +90,7 @@ export default abstract class OCPIClient { await this.deleteCredentials(); // Save endpoint this.ocpiEndpoint.status = OCPIRegistrationStatus.UNREGISTERED; - await OCPIEndpointStorage.saveOcpiEndpoint(this.tenant.id, this.ocpiEndpoint); + await OCPIEndpointStorage.saveOcpiEndpoint(this.tenant, this.ocpiEndpoint); // Send success unregisterResult.statusCode = StatusCodes.OK; unregisterResult.statusText = ReasonPhrases.OK; @@ -140,7 +140,7 @@ export default abstract class OCPIClient { this.ocpiEndpoint.businessDetails = credential.business_details; // Save endpoint this.ocpiEndpoint.status = OCPIRegistrationStatus.REGISTERED; - await OCPIEndpointStorage.saveOcpiEndpoint(this.tenant.id, this.ocpiEndpoint); + await OCPIEndpointStorage.saveOcpiEndpoint(this.tenant, this.ocpiEndpoint); // Send success registerResult.statusCode = StatusCodes.OK; registerResult.statusText = ReasonPhrases.OK; diff --git a/src/client/ocpi/OCPIClientFactory.ts b/src/client/ocpi/OCPIClientFactory.ts index a1a4bc97d1..822864ec0b 100644 --- a/src/client/ocpi/OCPIClientFactory.ts +++ b/src/client/ocpi/OCPIClientFactory.ts @@ -75,7 +75,7 @@ export default class OCPIClientFactory { } static async getAvailableOcpiClient(tenant: Tenant, ocpiRole: OCPIRole): Promise { - const ocpiEndpoints = await OCPIEndpointStorage.getOcpiEndpoints(tenant.id, { role: ocpiRole }, Constants.DB_PARAMS_MAX_LIMIT); + const ocpiEndpoints = await OCPIEndpointStorage.getOcpiEndpoints(tenant, { role: ocpiRole }, Constants.DB_PARAMS_MAX_LIMIT); for (const ocpiEndpoint of ocpiEndpoints.result) { if (ocpiEndpoint.status === OCPIRegistrationStatus.REGISTERED) { const client = await OCPIClientFactory.getOcpiClient(tenant, ocpiEndpoint); @@ -85,7 +85,7 @@ export default class OCPIClientFactory { } static async getChargingStationClient(tenant: Tenant, chargingStation: ChargingStation): Promise { - const ocpiEndpoints = await OCPIEndpointStorage.getOcpiEndpoints(tenant.id, { role: OCPIRole.EMSP }, Constants.DB_PARAMS_MAX_LIMIT); + const ocpiEndpoints = await OCPIEndpointStorage.getOcpiEndpoints(tenant, { role: OCPIRole.EMSP }, Constants.DB_PARAMS_MAX_LIMIT); for (const ocpiEndpoint of ocpiEndpoints.result) { if (ocpiEndpoint.status === OCPIRegistrationStatus.REGISTERED) { const client = await OCPIClientFactory.getEmspOcpiClient(tenant, ocpiEndpoint); diff --git a/src/client/oicp/CpoOICPClient.ts b/src/client/oicp/CpoOICPClient.ts index ad3f1bd002..f6e4d503cf 100644 --- a/src/client/oicp/CpoOICPClient.ts +++ b/src/client/oicp/CpoOICPClient.ts @@ -210,7 +210,7 @@ export default class CpoOICPClient extends OICPClient { do { // Get all charging stations from tenant chargingStations = (await ChargingStationStorage.getChargingStations(this.tenant.id, - { siteIDs: [site.id], public: true }, { skip: currentChargingStationSkip, limit: Constants.DB_RECORD_COUNT_DEFAULT })).result; + { siteIDs: [site.id], public: true, withSiteArea: true }, { skip: currentChargingStationSkip, limit: Constants.DB_RECORD_COUNT_DEFAULT })).result; if (!Utils.isEmptyArray(chargingStations)) { // Convert (public) charging stations to OICP EVSEs const evses = await OICPUtils.convertChargingStationsToEVSEs(this.tenant, site, chargingStations, options); @@ -358,7 +358,7 @@ export default class CpoOICPClient extends OICPClient { do { // Get all charging stations from tenant chargingStations = (await ChargingStationStorage.getChargingStations(this.tenant.id, - { siteIDs: [site.id], public: true }, { skip: currentChargingStationSkip, limit: Constants.DB_RECORD_COUNT_DEFAULT })).result; + { siteIDs: [site.id], public: true, withSiteArea: true }, { skip: currentChargingStationSkip, limit: Constants.DB_RECORD_COUNT_DEFAULT })).result; if (!Utils.isEmptyArray(chargingStations)) { // Convert (public) charging stations to OICP EVSE Statuses const evseStatuses = OICPUtils.convertChargingStationsToEvseStatuses(chargingStations, options); diff --git a/src/integration/refund/concur b/src/integration/refund/concur index 14450d0357..bc0e79600d 160000 --- a/src/integration/refund/concur +++ b/src/integration/refund/concur @@ -1 +1 @@ -Subproject commit 14450d03571c917d96170e8c5de5e06668b223f5 +Subproject commit bc0e79600d27a630c08b0da6ee0a6154cf111cd3 diff --git a/src/integration/smart-charging/sap-smart-charging b/src/integration/smart-charging/sap-smart-charging index 91477d7837..bcc1a00888 160000 --- a/src/integration/smart-charging/sap-smart-charging +++ b/src/integration/smart-charging/sap-smart-charging @@ -1 +1 @@ -Subproject commit 91477d7837c0996071d6af9f6df362a0f2af9ba0 +Subproject commit bcc1a008884d19eda8aeccf8eb47fcf723ea86a8 diff --git a/src/migration/MigrationHandler.ts b/src/migration/MigrationHandler.ts index 7b8281351a..5692459ca6 100644 --- a/src/migration/MigrationHandler.ts +++ b/src/migration/MigrationHandler.ts @@ -1,3 +1,5 @@ +import AddCompanyIDToChargingStationsTask from './tasks/AddCompanyIDToChargingStationsTask'; +import AddCompanyIDToTransactionsTask from './tasks/AddCompanyIDToTransactionsTask'; import Constants from '../utils/Constants'; import { LockEntity } from '../types/Locking'; import LockingManager from '../locking/LockingManager'; @@ -86,6 +88,8 @@ export default class MigrationHandler { private static createMigrationTasks(): MigrationTask[] { const currentMigrationTasks: MigrationTask[] = []; currentMigrationTasks.push(new RemoveDuplicateTagVisualIDsTask()); + currentMigrationTasks.push(new AddCompanyIDToTransactionsTask()); + currentMigrationTasks.push(new AddCompanyIDToChargingStationsTask()); return currentMigrationTasks; } diff --git a/src/migration/tasks/AddCompanyIDToChargingStationsTask.ts b/src/migration/tasks/AddCompanyIDToChargingStationsTask.ts new file mode 100644 index 0000000000..75521617d2 --- /dev/null +++ b/src/migration/tasks/AddCompanyIDToChargingStationsTask.ts @@ -0,0 +1,80 @@ +import Constants from '../../utils/Constants'; +import DatabaseUtils from '../../storage/mongodb/DatabaseUtils'; +import Logging from '../../utils/Logging'; +import MigrationTask from '../MigrationTask'; +import { ServerAction } from '../../types/Server'; +import SiteStorage from '../../storage/mongodb/SiteStorage'; +import Tenant from '../../types/Tenant'; +import TenantStorage from '../../storage/mongodb/TenantStorage'; +import Utils from '../../utils/Utils'; +import global from '../../types/GlobalType'; + +const MODULE_NAME = 'AddCompanyIDPropertToChargingStationsTask'; + +export default class AddCompanyIDToChargingStationsTask extends MigrationTask { + async migrate(): Promise { + const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); + for (const tenant of tenants.result) { + await this.migrateTenant(tenant); + } + } + + async migrateTenant(tenant: Tenant): Promise { + let updated = 0; + // Get all the Sites + const sites = (await SiteStorage.getSites(tenant, {}, Constants.DB_PARAMS_MAX_LIMIT, ['id', 'companyID'])).result; + if (!Utils.isEmptyArray(sites)) { + // Get all the Transactions without Company ID + const transactions = await global.database.getCollection(tenant.id, 'transactions') + .find({ + $or: [ + { companyID: { $exists: false } }, + { companyID: null }, + { companyID: '' } + ] + }) + .project({ id: 1, siteID: 1 }) + .toArray(); + if (!Utils.isEmptyArray(transactions)) { + for (const transaction of transactions) { + // Has a Site + if (!transaction.siteID) { + continue; + } + // Find the Site + const foundSite = sites.find((site) => transaction.siteID?.toHexString() === site.id); + if (foundSite?.companyID) { + await global.database.getCollection(tenant.id, 'transactions').updateOne( + { _id: transaction._id }, + { + $set: { companyID: DatabaseUtils.convertToObjectID(foundSite.companyID) } + } + ); + updated++; + } + } + } + } + // Log in the default tenant + if (updated > 0) { + await Logging.logDebug({ + tenantID: Constants.DEFAULT_TENANT, + module: MODULE_NAME, method: 'migrateTenant', + action: ServerAction.MIGRATION, + message: `${updated} Transactions(s) have been updated with Company ID in Tenant ${Utils.buildTenantName(tenant)}` + }); + } + } + + getVersion(): string { + return '1.0'; + } + + getName(): string { + return 'AddCompanyIDToChargingStationsTask'; + } + + isAsynchronous(): boolean { + return true; + } +} diff --git a/src/migration/tasks/AddCompanyIDToTransactionsTask.ts b/src/migration/tasks/AddCompanyIDToTransactionsTask.ts new file mode 100644 index 0000000000..2b60a0c990 --- /dev/null +++ b/src/migration/tasks/AddCompanyIDToTransactionsTask.ts @@ -0,0 +1,80 @@ +import Constants from '../../utils/Constants'; +import DatabaseUtils from '../../storage/mongodb/DatabaseUtils'; +import Logging from '../../utils/Logging'; +import MigrationTask from '../MigrationTask'; +import { ServerAction } from '../../types/Server'; +import SiteStorage from '../../storage/mongodb/SiteStorage'; +import Tenant from '../../types/Tenant'; +import TenantStorage from '../../storage/mongodb/TenantStorage'; +import Utils from '../../utils/Utils'; +import global from '../../types/GlobalType'; + +const MODULE_NAME = 'AddCompanyIDPropertToChargingStationsTask'; + +export default class AddCompanyIDToTransactionsTask extends MigrationTask { + async migrate(): Promise { + const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); + for (const tenant of tenants.result) { + await this.migrateTenant(tenant); + } + } + + async migrateTenant(tenant: Tenant): Promise { + let updated = 0; + // Get all the Sites + const sites = (await SiteStorage.getSites(tenant, {}, Constants.DB_PARAMS_MAX_LIMIT, ['id', 'companyID'])).result; + if (!Utils.isEmptyArray(sites)) { + // Get all the Charging Stations without Company ID + const chargingStations = await global.database.getCollection(tenant.id, 'chargingstations') + .find({ + $or: [ + { companyID: { $exists: false } }, + { companyID: null }, + { companyID: '' } + ] + }) + .project({ id: 1, siteID: 1 }) + .toArray(); + if (!Utils.isEmptyArray(chargingStations)) { + for (const chargingStation of chargingStations) { + // Has a Site + if (!chargingStation.siteID) { + continue; + } + // Find the Site + const foundSite = sites.find((site) => chargingStation.siteID?.toHexString() === site.id); + if (foundSite?.companyID) { + await global.database.getCollection(tenant.id, 'chargingstations').updateOne( + { _id: chargingStation._id }, + { + $set: { companyID: DatabaseUtils.convertToObjectID(foundSite.companyID) } + } + ); + updated++; + } + } + } + } + // Log in the default tenant + if (updated > 0) { + await Logging.logDebug({ + tenantID: Constants.DEFAULT_TENANT, + module: MODULE_NAME, method: 'migrateTenant', + action: ServerAction.MIGRATION, + message: `${updated} Charging Station(s) have been updated with Company ID in Tenant ${Utils.buildTenantName(tenant)}` + }); + } + } + + getVersion(): string { + return '1.0'; + } + + getName(): string { + return 'AddCompanyIDToTransactionsTask'; + } + + isAsynchronous(): boolean { + return true; + } +} diff --git a/src/migration/tasks/AddVisualIDPropertyToTagsTask.ts b/src/migration/tasks/AddVisualIDPropertyToTagsTask.ts deleted file mode 100644 index 7fd78fb484..0000000000 --- a/src/migration/tasks/AddVisualIDPropertyToTagsTask.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { FilterQuery, ObjectID } from 'mongodb'; - -import Constants from '../../utils/Constants'; -import Logging from '../../utils/Logging'; -import MigrationTask from '../MigrationTask'; -import { ServerAction } from '../../types/Server'; -import Tag from '../../types/Tag'; -import Tenant from '../../types/Tenant'; -import TenantStorage from '../../storage/mongodb/TenantStorage'; -import Utils from '../../utils/Utils'; -import global from '../../types/GlobalType'; - -const MODULE_NAME = 'AddVisualIDPropertyToTagsTask'; - -export default class AddVisualIDPropertyToTagsTask extends MigrationTask { - async migrate(): Promise { - const tenants = await TenantStorage.getTenants({}, Constants.DB_PARAMS_MAX_LIMIT); - for (const tenant of tenants.result) { - await this.migrateTenant(tenant); - } - } - - async migrateTenant(tenant: Tenant): Promise { - let tags: Tag[]; - let updated = 0; - const findFilter: FilterQuery = { - $or: [ - { visualID: { $exists: false } }, - { visualID: null }, - { visualID: '' } - ] - }; - do { - // Get the tags - tags = await global.database.getCollection(tenant.id, 'tags') - .find(findFilter) - .limit(Constants.BATCH_PAGE_SIZE) - .toArray(); - if (!Utils.isEmptyArray(tags)) { - for (const tag of tags) { - const visualID = new ObjectID().toHexString(); - await global.database.getCollection(tenant.id, 'tags').updateOne( - { _id: tag['_id'] }, - { $set: { visualID } } - ); - updated++; - } - } - } while (tags.length === Constants.BATCH_PAGE_SIZE); // Avoid infinite loop due to issues in the update process - // Log in the default tenant - if (updated > 0) { - await Logging.logDebug({ - tenantID: Constants.DEFAULT_TENANT, - module: MODULE_NAME, method: 'migrateTenant', - action: ServerAction.MIGRATION, - message: `${updated} Tag(s) visualID have been updated in Tenant ${Utils.buildTenantName(tenant)}` - }); - } - } - - getVersion(): string { - return '1.7'; - } - - getName(): string { - return 'AddVisualIDPropertyToTagsTask'; - } - - isAsynchronous(): boolean { - return true; - } -} diff --git a/src/scheduler/tasks/CheckChargingStationTemplateTask.ts b/src/scheduler/tasks/CheckChargingStationTemplateTask.ts index 1e730e8b10..b84b849474 100644 --- a/src/scheduler/tasks/CheckChargingStationTemplateTask.ts +++ b/src/scheduler/tasks/CheckChargingStationTemplateTask.ts @@ -51,7 +51,7 @@ export default class CheckChargingStationTemplateTask extends SchedulerTask { } // Get the charging stations const chargingStations = await ChargingStationStorage.getChargingStations(tenant.id, { - issuer: true + issuer: true, withSiteArea: true }, Constants.DB_PARAMS_MAX_LIMIT); // Update for (const chargingStation of chargingStations.result) { diff --git a/src/scheduler/tasks/CheckOfflineChargingStationsTask.ts b/src/scheduler/tasks/CheckOfflineChargingStationsTask.ts index f0609a1e86..acf63f42d0 100644 --- a/src/scheduler/tasks/CheckOfflineChargingStationsTask.ts +++ b/src/scheduler/tasks/CheckOfflineChargingStationsTask.ts @@ -25,8 +25,7 @@ export default class CheckOfflineChargingStationsTask extends SchedulerTask { // Compute the date some minutes ago const offlineSince = moment().subtract(Configuration.getChargingStationConfig().maxLastSeenIntervalSecs, 'seconds').toDate(); const chargingStations = await ChargingStationStorage.getChargingStations(tenant.id, { - issuer: true, - offlineSince + issuer: true, withSiteArea: true, offlineSince }, Constants.DB_PARAMS_MAX_LIMIT); if (chargingStations.count > 0) { for (let i = chargingStations.result.length - 1; i >= 0; i--) { diff --git a/src/scheduler/tasks/CheckPreparingSessionNotStartedTask.ts b/src/scheduler/tasks/CheckPreparingSessionNotStartedTask.ts index f355217033..2ee13e9c3e 100644 --- a/src/scheduler/tasks/CheckPreparingSessionNotStartedTask.ts +++ b/src/scheduler/tasks/CheckPreparingSessionNotStartedTask.ts @@ -23,7 +23,8 @@ export default class CheckPreparingSessionNotStartedTask extends SchedulerTask { // Get Charging Stations const chargingStations = await ChargingStationStorage.getChargingStations(tenant.id, { 'statusChangedBefore': moment().subtract(config.preparingStatusMaxMins, 'minutes').toDate(), - 'connectorStatuses': [ChargePointStatus.PREPARING] + 'connectorStatuses': [ChargePointStatus.PREPARING], + withSiteArea: true }, Constants.DB_PARAMS_MAX_LIMIT); for (const chargingStation of chargingStations.result) { // Get site owner and then send notification diff --git a/src/scheduler/tasks/ocpi/OCPICheckCdrsTask.ts b/src/scheduler/tasks/ocpi/OCPICheckCdrsTask.ts index 43ae32f05a..701a62ea39 100644 --- a/src/scheduler/tasks/ocpi/OCPICheckCdrsTask.ts +++ b/src/scheduler/tasks/ocpi/OCPICheckCdrsTask.ts @@ -23,7 +23,7 @@ export default class OCPICheckCdrsTask extends SchedulerTask { // Check if OCPI component is active if (Utils.isTenantComponentActive(tenant, TenantComponents.OCPI)) { // Get all available endpoints - const ocpiEndpoints = await OCPIEndpointStorage.getOcpiEndpoints(tenant.id, { role: OCPIRole.CPO }, Constants.DB_PARAMS_MAX_LIMIT); + const ocpiEndpoints = await OCPIEndpointStorage.getOcpiEndpoints(tenant, { role: OCPIRole.CPO }, Constants.DB_PARAMS_MAX_LIMIT); for (const ocpiEndpoint of ocpiEndpoints.result) { await this.processOCPIEndpoint(tenant, ocpiEndpoint); } diff --git a/src/scheduler/tasks/ocpi/OCPICheckLocationsTask.ts b/src/scheduler/tasks/ocpi/OCPICheckLocationsTask.ts index 24c6cc808c..74e723a30b 100644 --- a/src/scheduler/tasks/ocpi/OCPICheckLocationsTask.ts +++ b/src/scheduler/tasks/ocpi/OCPICheckLocationsTask.ts @@ -23,7 +23,7 @@ export default class OCPICheckLocationsTask extends SchedulerTask { // Check if OCPI component is active if (Utils.isTenantComponentActive(tenant, TenantComponents.OCPI)) { // Get all available endpoints - const ocpiEndpoints = await OCPIEndpointStorage.getOcpiEndpoints(tenant.id, { role: OCPIRole.CPO }, Constants.DB_PARAMS_MAX_LIMIT); + const ocpiEndpoints = await OCPIEndpointStorage.getOcpiEndpoints(tenant, { role: OCPIRole.CPO }, Constants.DB_PARAMS_MAX_LIMIT); for (const ocpiEndpoint of ocpiEndpoints.result) { await this.processOCPIEndpoint(tenant, ocpiEndpoint); } diff --git a/src/scheduler/tasks/ocpi/OCPICheckSessionsTask.ts b/src/scheduler/tasks/ocpi/OCPICheckSessionsTask.ts index e53309afbf..f2b5a6eb54 100644 --- a/src/scheduler/tasks/ocpi/OCPICheckSessionsTask.ts +++ b/src/scheduler/tasks/ocpi/OCPICheckSessionsTask.ts @@ -23,7 +23,7 @@ export default class OCPICheckSessionsTask extends SchedulerTask { // Check if OCPI component is active if (Utils.isTenantComponentActive(tenant, TenantComponents.OCPI)) { // Get all available endpoints - const ocpiEndpoints = await OCPIEndpointStorage.getOcpiEndpoints(tenant.id, { role: OCPIRole.CPO }, Constants.DB_PARAMS_MAX_LIMIT); + const ocpiEndpoints = await OCPIEndpointStorage.getOcpiEndpoints(tenant, { role: OCPIRole.CPO }, Constants.DB_PARAMS_MAX_LIMIT); for (const ocpiEndpoint of ocpiEndpoints.result) { await this.processOCPIEndpoint(tenant, ocpiEndpoint); } diff --git a/src/scheduler/tasks/ocpi/OCPIGetCdrsTask.ts b/src/scheduler/tasks/ocpi/OCPIGetCdrsTask.ts index 639cdd3ed4..4616f6d462 100644 --- a/src/scheduler/tasks/ocpi/OCPIGetCdrsTask.ts +++ b/src/scheduler/tasks/ocpi/OCPIGetCdrsTask.ts @@ -23,7 +23,7 @@ export default class OCPIGetCdrsTask extends SchedulerTask { // Check if OCPI component is active if (Utils.isTenantComponentActive(tenant, TenantComponents.OCPI)) { // Get all available endpoints - const ocpiEndpoints = await OCPIEndpointStorage.getOcpiEndpoints(tenant.id, { role: OCPIRole.EMSP }, Constants.DB_PARAMS_MAX_LIMIT); + const ocpiEndpoints = await OCPIEndpointStorage.getOcpiEndpoints(tenant, { role: OCPIRole.EMSP }, Constants.DB_PARAMS_MAX_LIMIT); for (const ocpiEndpoint of ocpiEndpoints.result) { await this.processOCPIEndpoint(tenant, ocpiEndpoint); } diff --git a/src/scheduler/tasks/ocpi/OCPIGetLocationsTask.ts b/src/scheduler/tasks/ocpi/OCPIGetLocationsTask.ts index 74a596e350..4f1e5ab25e 100644 --- a/src/scheduler/tasks/ocpi/OCPIGetLocationsTask.ts +++ b/src/scheduler/tasks/ocpi/OCPIGetLocationsTask.ts @@ -24,7 +24,7 @@ export default class OCPIGetLocationsTask extends SchedulerTask { // Check if OCPI component is active if (Utils.isTenantComponentActive(tenant, TenantComponents.OCPI)) { // Get all available endpoints - const ocpiEndpoints = await OCPIEndpointStorage.getOcpiEndpoints(tenant.id, { role: OCPIRole.EMSP }, Constants.DB_PARAMS_MAX_LIMIT); + const ocpiEndpoints = await OCPIEndpointStorage.getOcpiEndpoints(tenant, { role: OCPIRole.EMSP }, Constants.DB_PARAMS_MAX_LIMIT); for (const ocpiEndpoint of ocpiEndpoints.result) { await this.processOCPIEndpoint(tenant, ocpiEndpoint, config); } diff --git a/src/scheduler/tasks/ocpi/OCPIGetSessionsTask.ts b/src/scheduler/tasks/ocpi/OCPIGetSessionsTask.ts index 00b8ca2eed..1944d4ead3 100644 --- a/src/scheduler/tasks/ocpi/OCPIGetSessionsTask.ts +++ b/src/scheduler/tasks/ocpi/OCPIGetSessionsTask.ts @@ -23,7 +23,7 @@ export default class OCPIGetSessionsTask extends SchedulerTask { // Check if OCPI component is active if (Utils.isTenantComponentActive(tenant, TenantComponents.OCPI)) { // Get all available endpoints - const ocpiEndpoints = await OCPIEndpointStorage.getOcpiEndpoints(tenant.id, { role: OCPIRole.EMSP }, Constants.DB_PARAMS_MAX_LIMIT); + const ocpiEndpoints = await OCPIEndpointStorage.getOcpiEndpoints(tenant, { role: OCPIRole.EMSP }, Constants.DB_PARAMS_MAX_LIMIT); for (const ocpiEndpoint of ocpiEndpoints.result) { await this.processOCPIEndpoint(tenant, ocpiEndpoint); } diff --git a/src/scheduler/tasks/ocpi/OCPIGetTokensTask.ts b/src/scheduler/tasks/ocpi/OCPIGetTokensTask.ts index d1bc0d1938..72cce3d9f3 100644 --- a/src/scheduler/tasks/ocpi/OCPIGetTokensTask.ts +++ b/src/scheduler/tasks/ocpi/OCPIGetTokensTask.ts @@ -24,7 +24,7 @@ export default class OCPIGetTokensTask extends SchedulerTask { // Check if OCPI component is active if (Utils.isTenantComponentActive(tenant, TenantComponents.OCPI)) { // Get all available endpoints - const ocpiEndpoints = await OCPIEndpointStorage.getOcpiEndpoints(tenant.id, { role: OCPIRole.CPO }, Constants.DB_PARAMS_MAX_LIMIT); + const ocpiEndpoints = await OCPIEndpointStorage.getOcpiEndpoints(tenant, { role: OCPIRole.CPO }, Constants.DB_PARAMS_MAX_LIMIT); for (const ocpiEndpoint of ocpiEndpoints.result) { await this.processOCPIEndpoint(tenant, ocpiEndpoint, config); } diff --git a/src/scheduler/tasks/ocpi/OCPIPushEVSEStatusesTask.ts b/src/scheduler/tasks/ocpi/OCPIPushEVSEStatusesTask.ts index 4984a77e5c..692815715a 100644 --- a/src/scheduler/tasks/ocpi/OCPIPushEVSEStatusesTask.ts +++ b/src/scheduler/tasks/ocpi/OCPIPushEVSEStatusesTask.ts @@ -24,7 +24,7 @@ export default class OCPIPushEVSEStatusesTask extends SchedulerTask { // Check if OCPI component is active if (Utils.isTenantComponentActive(tenant, TenantComponents.OCPI)) { // Get all available endpoints - const ocpiEndpoints = await OCPIEndpointStorage.getOcpiEndpoints(tenant.id, { role: OCPIRole.CPO }, Constants.DB_PARAMS_MAX_LIMIT); + const ocpiEndpoints = await OCPIEndpointStorage.getOcpiEndpoints(tenant, { role: OCPIRole.CPO }, Constants.DB_PARAMS_MAX_LIMIT); for (const ocpiEndpoint of ocpiEndpoints.result) { await this.processOCPIEndpoint(tenant, ocpiEndpoint, config); } diff --git a/src/server/ocpi/AbstractOCPIService.ts b/src/server/ocpi/AbstractOCPIService.ts index 30614bdffc..e854fad476 100644 --- a/src/server/ocpi/AbstractOCPIService.ts +++ b/src/server/ocpi/AbstractOCPIService.ts @@ -190,7 +190,7 @@ export default abstract class AbstractOCPIService { ocpiError: OCPIStatusCode.CODE_3000_GENERIC_SERVER_ERROR }); } - const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpointByLocalToken(tenant.id, token); + const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpointByLocalToken(tenant, token); // Check if endpoint is found if (!ocpiEndpoint) { throw new AppError({ diff --git a/src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/OCPIUtilsService.ts b/src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/OCPIUtilsService.ts index 73aeae9bbf..9ab78329c6 100644 --- a/src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/OCPIUtilsService.ts +++ b/src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/OCPIUtilsService.ts @@ -364,7 +364,7 @@ export default class OCPIUtilsService { const evses: OCPIEvse[] = []; // Convert charging stations to evse(s) const chargingStations = await ChargingStationStorage.getChargingStations(tenant.id, - { ...dbFilters, siteIDs: [ siteID ], public: true, issuer: true }, + { ...dbFilters, siteIDs: [ siteID ], public: true, issuer: true, withSiteArea: true }, dbParams ?? Constants.DB_PARAMS_MAX_LIMIT, [ 'id', 'chargePoints', 'connectors', 'coordinates', 'lastSeen', 'siteAreaID', 'siteID' ]); for (const chargingStation of chargingStations.result) { @@ -883,7 +883,7 @@ export default class OCPIUtilsService { // F3C Baume les dames case '60990f1cc48de10014ea4fdc': switch (chargingStation?.id) { - case 'F3CBaume-CAHORS24DC': + case 'F3CBaume-CAHORS25DC': return 'Tarif_EVSE_DC'; case 'F3CBaume-LAFON22AC': return 'Tarif_EVSE_AC'; diff --git a/src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/credentials/CredentialsEndpoint.ts b/src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/credentials/CredentialsEndpoint.ts index e0716c5302..83acf2bc35 100644 --- a/src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/credentials/CredentialsEndpoint.ts +++ b/src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/credentials/CredentialsEndpoint.ts @@ -54,7 +54,7 @@ export default class CredentialsEndpoint extends AbstractEndpoint { detailedMessages: { token } }); // Get ocpiEndpoints based on the given token - const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpointByLocalToken(tenant.id, token); + const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpointByLocalToken(tenant, token); // Check if ocpiEndpoint available if (!ocpiEndpoint || ocpiEndpoint.status === OCPIRegistrationStatus.UNREGISTERED) { throw new AppError({ @@ -69,7 +69,7 @@ export default class CredentialsEndpoint extends AbstractEndpoint { // Save ocpi endpoint ocpiEndpoint.status = OCPIRegistrationStatus.UNREGISTERED; ocpiEndpoint.backgroundPatchJob = false; - await OCPIEndpointStorage.saveOcpiEndpoint(tenant.id, ocpiEndpoint); + await OCPIEndpointStorage.saveOcpiEndpoint(tenant, ocpiEndpoint); return OCPIUtils.success(); } @@ -111,7 +111,7 @@ export default class CredentialsEndpoint extends AbstractEndpoint { detailedMessages: { token } }); // Get ocpiEndpoints based on the given token - const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpointByLocalToken(tenant.id, token); + const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpointByLocalToken(tenant, token); // Check if ocpiEndpoint available if (!ocpiEndpoint) { throw new AppError({ @@ -234,7 +234,7 @@ export default class CredentialsEndpoint extends AbstractEndpoint { ocpiEndpoint.localToken = OCPIUtils.generateLocalToken(tenant.subdomain); ocpiEndpoint.status = OCPIRegistrationStatus.REGISTERED; // Save ocpi endpoint - await OCPIEndpointStorage.saveOcpiEndpoint(tenant.id, ocpiEndpoint); + await OCPIEndpointStorage.saveOcpiEndpoint(tenant, ocpiEndpoint); // Get base url const versionUrl = this.getServiceUrl(req) + AbstractOCPIService.VERSIONS_PATH; // Build credential object diff --git a/src/server/ocpp/services/OCPPService.ts b/src/server/ocpp/services/OCPPService.ts index b7cd5a11ab..51bdd6b204 100644 --- a/src/server/ocpp/services/OCPPService.ts +++ b/src/server/ocpp/services/OCPPService.ts @@ -698,9 +698,10 @@ export default class OCPPService { private async processConnectorStatusNotification(tenant: Tenant, chargingStation: ChargingStation, statusNotification: OCPPStatusNotificationRequestExtended) { // Get Connector - const connector = await this.checkAndGetConnectorFromStatusNotification(tenant, chargingStation, statusNotification); + const { connector, newConnector } = await this.checkAndGetConnectorFromStatusNotification( + tenant, chargingStation, statusNotification); // Status must be different - if (!await this.hasStatusNotificationChanged(tenant, chargingStation, connector, statusNotification)) { + if (!newConnector && !await this.hasStatusNotificationChanged(tenant, chargingStation, connector, statusNotification)) { return; } // Check last Transaction @@ -806,9 +807,12 @@ export default class OCPPService { } private async checkAndGetConnectorFromStatusNotification(tenant: Tenant, chargingStation: ChargingStation, - statusNotification: OCPPStatusNotificationRequestExtended): Promise { + statusNotification: OCPPStatusNotificationRequestExtended): Promise<{ connector: Connector, newConnector: boolean }> { + let newConnector = false; let foundConnector = Utils.getConnectorFromID(chargingStation, statusNotification.connectorId); if (!foundConnector) { + // To be saved + newConnector = true; // Check backup first foundConnector = Utils.getBackupConnectorFromID(chargingStation, statusNotification.connectorId); if (foundConnector) { @@ -838,7 +842,7 @@ export default class OCPPService { } } } - return foundConnector; + return { connector: foundConnector, newConnector }; } private async checkAndUpdateLastCompletedTransaction(tenant: Tenant, chargingStation: ChargingStation, @@ -1683,9 +1687,9 @@ export default class OCPPService { // Set the Site Area ID startTransaction.siteAreaID = chargingStation.siteAreaID; // Set the Site ID. ChargingStation$siteArea$site checked by TagIDAuthorized. - const site = chargingStation.siteArea ? chargingStation.siteArea.site : null; - if (site) { - startTransaction.siteID = site.id; + if (chargingStation.site) { + startTransaction.siteID = chargingStation.site.id; + startTransaction.companyID = chargingStation.site.companyID; } } } @@ -1698,8 +1702,9 @@ export default class OCPPService { tagID: startTransaction.idTag, timezone: startTransaction.timezone, userID: startTransaction.userID, - siteAreaID: startTransaction.siteAreaID, + companyID: startTransaction.companyID, siteID: startTransaction.siteID, + siteAreaID: startTransaction.siteAreaID, connectorId: startTransaction.connectorId, meterStart: startTransaction.meterStart, timestamp: Utils.convertToDate(startTransaction.timestamp), @@ -1787,10 +1792,11 @@ export default class OCPPService { newChargingStation.registrationStatus = RegistrationStatus.ACCEPTED; // Assign to Site Area if (token.siteAreaID) { - const siteArea = await SiteAreaStorage.getSiteArea(tenant.id, token.siteAreaID); + const siteArea = await SiteAreaStorage.getSiteArea(tenant.id, token.siteAreaID, { withSite: true }); if (siteArea) { - newChargingStation.siteAreaID = token.siteAreaID; + newChargingStation.companyID = siteArea.site?.companyID; newChargingStation.siteID = siteArea.siteID; + newChargingStation.siteAreaID = token.siteAreaID; // Set the same coordinates if (siteArea?.address?.coordinates?.length === 2) { newChargingStation.coordinates = siteArea.address.coordinates; diff --git a/src/server/ocpp/utils/OCPPUtils.ts b/src/server/ocpp/utils/OCPPUtils.ts index 76f1f51cd4..fb8b0917d4 100644 --- a/src/server/ocpp/utils/OCPPUtils.ts +++ b/src/server/ocpp/utils/OCPPUtils.ts @@ -1029,7 +1029,7 @@ export default class OCPPUtils { } else { // Compute it for Charging Stations const chargingStationsOfSiteArea = await ChargingStationStorage.getChargingStations(tenant.id, - { siteAreaIDs: [siteArea.id] }, Constants.DB_PARAMS_MAX_LIMIT); + { siteAreaIDs: [siteArea.id], withSiteArea: true }, Constants.DB_PARAMS_MAX_LIMIT); for (const chargingStationOfSiteArea of chargingStationsOfSiteArea.result) { if (Utils.objectHasProperty(chargingStationOfSiteArea, 'connectors')) { for (const connector of chargingStationOfSiteArea.connectors) { diff --git a/src/server/rest/CentralRestServerService.ts b/src/server/rest/CentralRestServerService.ts index c663dda1b3..3d116fd665 100644 --- a/src/server/rest/CentralRestServerService.ts +++ b/src/server/rest/CentralRestServerService.ts @@ -34,11 +34,31 @@ class RequestMapper { switch (httpVerb) { // Create case 'POST': + this.registerOneActionManyPaths( + async (action: ServerAction, req: Request, res: Response, next: NextFunction) => { + // Delegate + await ChargingStationService.handleAction(action, req, res, next); + }, + ServerAction.CHARGING_STATION_CLEAR_CACHE, + ServerAction.CHARGING_STATION_GET_CONFIGURATION, + ServerAction.CHARGING_STATION_CHANGE_CONFIGURATION, + ServerAction.CHARGING_STATION_REMOTE_STOP_TRANSACTION, + ServerAction.CHARGING_STATION_REMOTE_START_TRANSACTION, + ServerAction.CHARGING_STATION_UNLOCK_CONNECTOR, + ServerAction.CHARGING_STATION_RESET, + ServerAction.CHARGING_STATION_SET_CHARGING_PROFILE, + ServerAction.CHARGING_STATION_GET_COMPOSITE_SCHEDULE, + ServerAction.CHARGING_STATION_CLEAR_CHARGING_PROFILE, + ServerAction.CHARGING_STATION_GET_DIAGNOSTICS, + ServerAction.CHARGING_STATION_CHANGE_AVAILABILITY, + ServerAction.CHARGING_STATION_UPDATE_FIRMWARE + ); // Register REST actions this.registerJsonActionsPaths({ [ServerAction.ADD_CHARGING_STATIONS_TO_SITE_AREA]: SiteAreaService.handleAssignChargingStationsToSiteArea.bind(this), [ServerAction.REMOVE_CHARGING_STATIONS_FROM_SITE_AREA]: SiteAreaService.handleAssignChargingStationsToSiteArea.bind(this), [ServerAction.REGISTRATION_TOKEN_CREATE]: RegistrationTokenService.handleCreateRegistrationToken.bind(this), + [ServerAction.USER_CREATE]: UserService.handleCreateUser.bind(this), [ServerAction.COMPANY_CREATE]: CompanyService.handleCreateCompany.bind(this), [ServerAction.ADD_ASSET_TO_SITE_AREA]: SiteAreaService.handleAssignAssetsToSiteArea.bind(this), [ServerAction.REMOVE_ASSET_TO_SITE_AREA]: SiteAreaService.handleAssignAssetsToSiteArea.bind(this), @@ -47,6 +67,8 @@ class RequestMapper { [ServerAction.SITE_CREATE]: SiteService.handleCreateSite.bind(this), [ServerAction.ADD_USERS_TO_SITE]: SiteService.handleAssignUsersToSite.bind(this), [ServerAction.REMOVE_USERS_FROM_SITE]: SiteService.handleAssignUsersToSite.bind(this), + [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.TRANSACTIONS_REFUND]: TransactionService.handleRefundTransactions.bind(this), [ServerAction.TRANSACTION_PUSH_CDR]: TransactionService.handlePushTransactionCdr.bind(this), @@ -80,6 +102,7 @@ class RequestMapper { [ServerAction.CAR_CREATE]: CarService.handleCreateCar.bind(this), [ServerAction.TAG_CREATE]: TagService.handleCreateTag.bind(this), [ServerAction.END_USER_REPORT_ERROR]: NotificationService.handleEndUserReportError.bind(this), + [ServerAction.USERS_IMPORT]: UserService.handleImportUsers.bind(this), [ServerAction.TAGS_IMPORT]: TagService.handleImportTags.bind(this), }); break; @@ -99,8 +122,18 @@ class RequestMapper { [ServerAction.CAR]: CarService.handleGetCar.bind(this), [ServerAction.CAR_USERS]: CarService.handleGetCarUsers.bind(this), [ServerAction.CAR_CATALOG_IMAGES]: CarService.handleGetCarCatalogImages.bind(this), + [ServerAction.CHARGING_STATIONS_EXPORT]: ChargingStationService.handleExportChargingStations.bind(this), + [ServerAction.CHARGING_STATIONS_OCPP_PARAMS_EXPORT]: ChargingStationService.handleExportChargingStationsOCPPParams.bind(this), + [ServerAction.CHARGING_STATION]: ChargingStationService.handleGetChargingStation.bind(this), + [ServerAction.CHECK_SMART_CHARGING_CONNECTION]: ChargingStationService.handleCheckSmartChargingConnection.bind(this), + [ServerAction.CHARGING_PROFILES]: ChargingStationService.handleGetChargingProfiles.bind(this), + [ServerAction.TRIGGER_SMART_CHARGING]: ChargingStationService.handleTriggerSmartCharging.bind(this), + [ServerAction.GENERATE_QR_CODE_FOR_CONNECTOR]: ChargingStationService.handleGenerateQrCodeForConnector.bind(this), + [ServerAction.CHARGING_STATION_DOWNLOAD_QR_CODE_PDF]: ChargingStationService.handleDownloadQrCodesPdf.bind(this), [ServerAction.REGISTRATION_TOKENS]: RegistrationTokenService.handleGetRegistrationTokens.bind(this), [ServerAction.REGISTRATION_TOKEN]: RegistrationTokenService.handleGetRegistrationToken.bind(this), + [ServerAction.STATUS_NOTIFICATIONS]: ChargingStationService.handleGetStatusNotifications.bind(this), + [ServerAction.BOOT_NOTIFICATION]: ChargingStationService.handleGetBootNotifications.bind(this), [ServerAction.COMPANIES]: CompanyService.handleGetCompanies.bind(this), [ServerAction.COMPANY]: CompanyService.handleGetCompany.bind(this), [ServerAction.ASSETS]: AssetService.handleGetAssets.bind(this), @@ -119,10 +152,16 @@ class RequestMapper { [ServerAction.SITE_AREA]: SiteAreaService.handleGetSiteArea.bind(this), [ServerAction.SITE_AREA_CONSUMPTION]: SiteAreaService.handleGetSiteAreaConsumption.bind(this), [ServerAction.USERS]: UserService.handleGetUsers.bind(this), + [ServerAction.USER_SITES]: UserService.handleGetSites.bind(this), + [ServerAction.USERS_IN_ERROR]: UserService.handleGetUsersInError.bind(this), + [ServerAction.USER_IMAGE]: UserService.handleGetUserImage.bind(this), + [ServerAction.USER]: UserService.handleGetUser.bind(this), + [ServerAction.USERS_EXPORT]: UserService.handleExportUsers.bind(this), [ServerAction.NOTIFICATIONS]: NotificationService.handleGetNotifications.bind(this), [ServerAction.TAGS]: TagService.handleGetTags.bind(this), [ServerAction.TAG]: TagService.handleGetTag.bind(this), [ServerAction.TAGS_EXPORT]: TagService.handleExportTags.bind(this), + [ServerAction.USER_DEFAULT_TAG_CAR]: UserService.handleGetUserDefaultTagCar.bind(this), [ServerAction.TRANSACTIONS_COMPLETED]: TransactionService.handleGetTransactionsCompleted.bind(this), [ServerAction.TRANSACTIONS_TO_REFUND]: TransactionService.handleGetTransactionsToRefund.bind(this), [ServerAction.TRANSACTIONS_TO_REFUND_EXPORT]: TransactionService.handleExportTransactionsToRefund.bind(this), @@ -148,6 +187,8 @@ class RequestMapper { [ServerAction.CHARGING_STATION_TRANSACTIONS]: TransactionService.handleGetChargingStationTransactions.bind(this), [ServerAction.TRANSACTION]: TransactionService.handleGetTransaction.bind(this), [ServerAction.TRANSACTION_CONSUMPTION]: TransactionService.handleGetTransactionConsumption.bind(this), + [ServerAction.CHARGING_STATIONS_OCPP_PARAMETERS]: ChargingStationService.handleGetChargingStationOcppParameters.bind(this), + [ServerAction.CHARGING_STATIONS_IN_ERROR]: ChargingStationService.handleGetChargingStationsInError.bind(this), [ServerAction.SETTING_BY_IDENTIFIER]: SettingService.handleGetSettingByIdentifier.bind(this), [ServerAction.SETTINGS]: SettingService.handleGetSettings.bind(this), [ServerAction.SETTING]: SettingService.handleGetSetting.bind(this), @@ -172,6 +213,11 @@ class RequestMapper { case 'PUT': // Register REST actions this.registerJsonActionsPaths({ + [ServerAction.USER_UPDATE]: UserService.handleUpdateUser.bind(this), + [ServerAction.USER_UPDATE_MOBILE_TOKEN]: UserService.handleUpdateUserMobileToken.bind(this), + [ServerAction.CHARGING_STATION_UPDATE_PARAMS]: ChargingStationService.handleUpdateChargingStationParams.bind(this), + [ServerAction.CHARGING_STATION_LIMIT_POWER]: ChargingStationService.handleChargingStationLimitPower.bind(this), + [ServerAction.CHARGING_PROFILE_UPDATE]: ChargingStationService.handleUpdateChargingProfile.bind(this), [ServerAction.TENANT_UPDATE]: TenantService.handleUpdateTenant.bind(this), [ServerAction.SITE_UPDATE]: SiteService.handleUpdateSite.bind(this), [ServerAction.SITE_AREA_UPDATE]: SiteAreaService.handleUpdateSiteArea.bind(this), @@ -200,6 +246,7 @@ class RequestMapper { case 'DELETE': // Register REST actions this.registerJsonActionsPaths({ + [ServerAction.USER_DELETE]: UserService.handleDeleteUser.bind(this), [ServerAction.TENANT_DELETE]: TenantService.handleDeleteTenant.bind(this), [ServerAction.REGISTRATION_TOKEN_DELETE]: RegistrationTokenService.handleDeleteRegistrationToken.bind(this), [ServerAction.REGISTRATION_TOKEN_REVOKE]: RegistrationTokenService.handleRevokeRegistrationToken.bind(this), @@ -207,6 +254,8 @@ class RequestMapper { [ServerAction.SITE_AREA_DELETE]: SiteAreaService.handleDeleteSiteArea.bind(this), [ServerAction.COMPANY_DELETE]: CompanyService.handleDeleteCompany.bind(this), [ServerAction.ASSET_DELETE]: AssetService.handleDeleteAsset.bind(this), + [ServerAction.CHARGING_STATION_DELETE]: ChargingStationService.handleDeleteChargingStation.bind(this), + [ServerAction.CHARGING_PROFILE_DELETE]: ChargingStationService.handleDeleteChargingProfile.bind(this), [ServerAction.TRANSACTION_DELETE]: TransactionService.handleDeleteTransaction.bind(this), [ServerAction.TRANSACTIONS_DELETE]: TransactionService.handleDeleteTransactions.bind(this), [ServerAction.INTEGRATION_CONNECTION_DELETE]: ConnectionService.handleDeleteConnection.bind(this), @@ -284,6 +333,10 @@ export default class CentralRestServerService { case ServerAction.TENANT_LOGO: await TenantService.handleGetTenantLogo(action, req, res, next); break; + // Firmware Download + case ServerAction.FIRMWARE_DOWNLOAD: + await ChargingStationService.handleGetFirmware(action, req, res, next); + break; default: // Delegate await UtilsService.handleUnknownAction(action, req, res, next); @@ -332,3 +385,339 @@ export default class CentralRestServerService { } } } + +// To uncomment back when mobile V2 will be available in Android +// import { NextFunction, Request, Response } from 'express'; + +// import AssetService from './v1/service/AssetService'; +// import BillingService from './v1/service/BillingService'; +// import CarService from './v1/service/CarService'; +// import ChargingStationService from './v1/service/ChargingStationService'; +// import CompanyService from './v1/service/CompanyService'; +// import ConnectionService from './v1/service/ConnectionService'; +// import Logging from '../../utils/Logging'; +// import LoggingService from './v1/service/LoggingService'; +// import NotificationService from './v1/service/NotificationService'; +// import OCPIEndpointService from './v1/service/OCPIEndpointService'; +// import OICPEndpointService from './v1/service/OICPEndpointService'; +// import RegistrationTokenService from './v1/service/RegistrationTokenService'; +// import { ServerAction } from '../../types/Server'; +// import SessionHashService from './v1/service/SessionHashService'; +// import SettingService from './v1/service/SettingService'; +// import SiteAreaService from './v1/service/SiteAreaService'; +// import SiteService from './v1/service/SiteService'; +// import StatisticService from './v1/service/StatisticService'; +// import { StatusCodes } from 'http-status-codes'; +// import TagService from './v1/service/TagService'; +// import TenantService from './v1/service/TenantService'; +// import TransactionService from './v1/service/TransactionService'; +// import UserService from './v1/service/UserService'; +// import UtilsService from './v1/service/UtilsService'; + +// class RequestMapper { +// private static instances = new Map(); +// private paths = new Map(); +// private actions = new Array<(action: ServerAction, req: Request, res: Response, next: NextFunction) => void|Promise>(); + +// private constructor(httpVerb: string) { +// switch (httpVerb) { +// // Create +// case 'POST': +// // Register REST actions +// this.registerJsonActionsPaths({ +// [ServerAction.ADD_CHARGING_STATIONS_TO_SITE_AREA]: SiteAreaService.handleAssignChargingStationsToSiteArea.bind(this), +// [ServerAction.REMOVE_CHARGING_STATIONS_FROM_SITE_AREA]: SiteAreaService.handleAssignChargingStationsToSiteArea.bind(this), +// [ServerAction.REGISTRATION_TOKEN_CREATE]: RegistrationTokenService.handleCreateRegistrationToken.bind(this), +// [ServerAction.COMPANY_CREATE]: CompanyService.handleCreateCompany.bind(this), +// [ServerAction.ADD_ASSET_TO_SITE_AREA]: SiteAreaService.handleAssignAssetsToSiteArea.bind(this), +// [ServerAction.REMOVE_ASSET_TO_SITE_AREA]: SiteAreaService.handleAssignAssetsToSiteArea.bind(this), +// [ServerAction.ASSET_CREATE]: AssetService.handleCreateAsset.bind(this), +// [ServerAction.TENANT_CREATE]: TenantService.handleCreateTenant.bind(this), +// [ServerAction.SITE_CREATE]: SiteService.handleCreateSite.bind(this), +// [ServerAction.ADD_USERS_TO_SITE]: SiteService.handleAssignUsersToSite.bind(this), +// [ServerAction.REMOVE_USERS_FROM_SITE]: SiteService.handleAssignUsersToSite.bind(this), +// [ServerAction.SITE_AREA_CREATE]: SiteAreaService.handleCreateSiteArea.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), +// [ServerAction.BILLING_SYNCHRONIZE_USER]: BillingService.handleSynchronizeUser.bind(this), +// [ServerAction.BILLING_FORCE_SYNCHRONIZE_USER]: BillingService.handleForceSynchronizeUser.bind(this), +// [ServerAction.BILLING_SYNCHRONIZE_INVOICES]: BillingService.handleSynchronizeInvoices.bind(this), +// [ServerAction.BILLING_FORCE_SYNCHRONIZE_USER_INVOICES]: BillingService.handleForceSynchronizeUserInvoices.bind(this), +// [ServerAction.BILLING_CREATE_TRANSACTION_INVOICE]: BillingService.handleCreateTransactionInvoice.bind(this), +// [ServerAction.BILLING_SETUP_PAYMENT_METHOD]: BillingService.handleBillingSetupPaymentMethod.bind(this), +// [ServerAction.OCPI_ENDPOINT_CREATE]: OCPIEndpointService.handleCreateOcpiEndpoint.bind(this), +// [ServerAction.OCPI_ENDPOINT_PING]: OCPIEndpointService.handlePingOcpiEndpoint.bind(this), +// [ServerAction.OCPI_ENDPOINT_CHECK_CDRS]: OCPIEndpointService.handleCheckCdrsEndpoint.bind(this), +// [ServerAction.OCPI_ENDPOINT_CHECK_LOCATIONS]: OCPIEndpointService.handleCheckLocationsEndpoint.bind(this), +// [ServerAction.OCPI_ENDPOINT_CHECK_SESSIONS]: OCPIEndpointService.handleCheckSessionsEndpoint.bind(this), +// [ServerAction.OCPI_ENDPOINT_PULL_CDRS]: OCPIEndpointService.handlePullCdrsEndpoint.bind(this), +// [ServerAction.OCPI_ENDPOINT_PULL_LOCATIONS]: OCPIEndpointService.handlePullLocationsEndpoint.bind(this), +// [ServerAction.OCPI_ENDPOINT_PULL_SESSIONS]: OCPIEndpointService.handlePullSessionsEndpoint.bind(this), +// [ServerAction.OCPI_ENDPOINT_PULL_TOKENS]: OCPIEndpointService.handlePullTokensEndpoint.bind(this), +// [ServerAction.OCPI_ENDPOINT_SEND_EVSE_STATUSES]: OCPIEndpointService.handlePushEVSEStatusesOcpiEndpoint.bind(this), +// [ServerAction.OCPI_ENDPOINT_SEND_TOKENS]: OCPIEndpointService.handlePushTokensOcpiEndpoint.bind(this), +// [ServerAction.OCPI_ENDPOINT_GENERATE_LOCAL_TOKEN]: OCPIEndpointService.handleGenerateLocalTokenOcpiEndpoint.bind(this), +// [ServerAction.OICP_ENDPOINT_CREATE]: OICPEndpointService.handleCreateOicpEndpoint.bind(this), +// [ServerAction.OICP_ENDPOINT_PING]: OICPEndpointService.handlePingOicpEndpoint.bind(this), +// [ServerAction.OICP_ENDPOINT_SEND_EVSE_STATUSES]: OICPEndpointService.handleSendEVSEStatusesOicpEndpoint.bind(this), +// [ServerAction.OICP_ENDPOINT_SEND_EVSES]: OICPEndpointService.handleSendEVSEsOicpEndpoint.bind(this), +// [ServerAction.INTEGRATION_CONNECTION_CREATE]: ConnectionService.handleCreateConnection.bind(this), +// [ServerAction.CHARGING_STATION_REQUEST_OCPP_PARAMETERS]: ChargingStationService.handleRequestChargingStationOcppParameters.bind(this), +// [ServerAction.CAR_CREATE]: CarService.handleCreateCar.bind(this), +// [ServerAction.TAG_CREATE]: TagService.handleCreateTag.bind(this), +// [ServerAction.END_USER_REPORT_ERROR]: NotificationService.handleEndUserReportError.bind(this), +// [ServerAction.TAGS_IMPORT]: TagService.handleImportTags.bind(this), +// }); +// break; + +// // Read +// case 'GET': +// // Register REST actions +// this.registerJsonActionsPaths({ +// [ServerAction.LOGGINGS]: LoggingService.handleGetLogs.bind(this), +// [ServerAction.LOGGING]: LoggingService.handleGetLog.bind(this), +// [ServerAction.LOGGINGS_EXPORT]: LoggingService.handleExportLogs.bind(this), +// [ServerAction.CHARGING_STATIONS]: ChargingStationService.handleGetChargingStations.bind(this), +// [ServerAction.CAR_CATALOGS]: CarService.handleGetCarCatalogs.bind(this), +// [ServerAction.CAR_CATALOG]: CarService.handleGetCarCatalog.bind(this), +// [ServerAction.CAR_MAKERS]: CarService.handleGetCarMakers.bind(this), +// [ServerAction.CARS]: CarService.handleGetCars.bind(this), +// [ServerAction.CAR]: CarService.handleGetCar.bind(this), +// [ServerAction.CAR_USERS]: CarService.handleGetCarUsers.bind(this), +// [ServerAction.CAR_CATALOG_IMAGES]: CarService.handleGetCarCatalogImages.bind(this), +// [ServerAction.REGISTRATION_TOKENS]: RegistrationTokenService.handleGetRegistrationTokens.bind(this), +// [ServerAction.REGISTRATION_TOKEN]: RegistrationTokenService.handleGetRegistrationToken.bind(this), +// [ServerAction.COMPANIES]: CompanyService.handleGetCompanies.bind(this), +// [ServerAction.COMPANY]: CompanyService.handleGetCompany.bind(this), +// [ServerAction.ASSETS]: AssetService.handleGetAssets.bind(this), +// [ServerAction.ASSET]: AssetService.handleGetAsset.bind(this), +// [ServerAction.ASSET_IMAGE]: AssetService.handleGetAssetImage.bind(this), +// [ServerAction.ASSETS_IN_ERROR]: AssetService.handleGetAssetsInError.bind(this), +// [ServerAction.CHECK_ASSET_CONNECTION]: AssetService.handleCheckAssetConnection.bind(this), +// [ServerAction.RETRIEVE_ASSET_CONSUMPTION]: AssetService.handleRetrieveConsumption.bind(this), +// [ServerAction.ASSET_CONSUMPTION]: AssetService.handleGetAssetConsumption.bind(this), +// [ServerAction.SITES]: SiteService.handleGetSites.bind(this), +// [ServerAction.SITE]: SiteService.handleGetSite.bind(this), +// [ServerAction.SITE_USERS]: SiteService.handleGetUsers.bind(this), +// [ServerAction.TENANTS]: TenantService.handleGetTenants.bind(this), +// [ServerAction.TENANT]: TenantService.handleGetTenant.bind(this), +// [ServerAction.SITE_AREAS]: SiteAreaService.handleGetSiteAreas.bind(this), +// [ServerAction.SITE_AREA]: SiteAreaService.handleGetSiteArea.bind(this), +// [ServerAction.SITE_AREA_CONSUMPTION]: SiteAreaService.handleGetSiteAreaConsumption.bind(this), +// [ServerAction.USERS]: UserService.handleGetUsers.bind(this), +// [ServerAction.NOTIFICATIONS]: NotificationService.handleGetNotifications.bind(this), +// [ServerAction.TAGS]: TagService.handleGetTags.bind(this), +// [ServerAction.TAG]: TagService.handleGetTag.bind(this), +// [ServerAction.TAGS_EXPORT]: TagService.handleExportTags.bind(this), +// [ServerAction.TRANSACTIONS_COMPLETED]: TransactionService.handleGetTransactionsCompleted.bind(this), +// [ServerAction.TRANSACTIONS_TO_REFUND]: TransactionService.handleGetTransactionsToRefund.bind(this), +// [ServerAction.TRANSACTIONS_TO_REFUND_EXPORT]: TransactionService.handleExportTransactionsToRefund.bind(this), +// [ServerAction.TRANSACTIONS_TO_REFUND_REPORTS]: TransactionService.handleGetRefundReports.bind(this), +// [ServerAction.TRANSACTIONS_EXPORT]: TransactionService.handleExportTransactions.bind(this), +// [ServerAction.TRANSACTIONS_ACTIVE]: TransactionService.handleGetTransactionsActive.bind(this), +// [ServerAction.TRANSACTIONS_IN_ERROR]: TransactionService.handleGetTransactionsInError.bind(this), +// [ServerAction.TRANSACTION_YEARS]: TransactionService.handleGetTransactionYears.bind(this), +// [ServerAction.REBUILD_TRANSACTION_CONSUMPTIONS]: TransactionService.handleRebuildTransactionConsumptions.bind(this), +// [ServerAction.UNASSIGNED_TRANSACTIONS_COUNT]: TransactionService.handleGetUnassignedTransactionsCount.bind(this), +// [ServerAction.TRANSACTION_OCPI_CDR_EXPORT]: TransactionService.handleExportTransactionOcpiCdr.bind(this), +// [ServerAction.CHARGING_STATION_CONSUMPTION_STATISTICS]: StatisticService.handleGetChargingStationConsumptionStatistics.bind(this), +// [ServerAction.CHARGING_STATION_USAGE_STATISTICS]: StatisticService.handleGetChargingStationUsageStatistics.bind(this), +// [ServerAction.CHARGING_STATION_INACTIVITY_STATISTICS]: StatisticService.handleGetChargingStationInactivityStatistics.bind(this), +// [ServerAction.CHARGING_STATION_TRANSACTIONS_STATISTICS]: StatisticService.handleGetChargingStationTransactionsStatistics.bind(this), +// [ServerAction.CHARGING_STATION_PRICING_STATISTICS]: StatisticService.handleGetChargingStationPricingStatistics.bind(this), +// [ServerAction.STATISTICS_EXPORT]: StatisticService.handleExportStatistics.bind(this), +// [ServerAction.USER_CONSUMPTION_STATISTICS]: StatisticService.handleGetUserConsumptionStatistics.bind(this), +// [ServerAction.USER_USAGE_STATISTICS]: StatisticService.handleGetUserUsageStatistics.bind(this), +// [ServerAction.USER_INACTIVITY_STATISTICS]: StatisticService.handleGetUserInactivityStatistics.bind(this), +// [ServerAction.USER_TRANSACTIONS_STATISTICS]: StatisticService.handleGetUserTransactionsStatistics.bind(this), +// [ServerAction.USER_PRICING_STATISTICS]: StatisticService.handleGetUserPricingStatistics.bind(this), +// [ServerAction.CHARGING_STATION_TRANSACTIONS]: TransactionService.handleGetChargingStationTransactions.bind(this), +// [ServerAction.TRANSACTION]: TransactionService.handleGetTransaction.bind(this), +// [ServerAction.TRANSACTION_CONSUMPTION]: TransactionService.handleGetTransactionConsumption.bind(this), +// [ServerAction.SETTING_BY_IDENTIFIER]: SettingService.handleGetSettingByIdentifier.bind(this), +// [ServerAction.SETTINGS]: SettingService.handleGetSettings.bind(this), +// [ServerAction.SETTING]: SettingService.handleGetSetting.bind(this), +// [ServerAction.CHECK_BILLING_CONNECTION]: BillingService.handleCheckBillingConnection.bind(this), +// [ServerAction.BILLING_TAXES]: BillingService.handleGetBillingTaxes.bind(this), +// [ServerAction.BILLING_INVOICES]: BillingService.handleGetInvoices.bind(this), +// [ServerAction.BILLING_DOWNLOAD_INVOICE]: BillingService.handleDownloadInvoice.bind(this), +// [ServerAction.BILLING_PAYMENT_METHODS]: BillingService.handleBillingGetPaymentMethods.bind(this), +// [ServerAction.OCPI_ENDPOINTS]: OCPIEndpointService.handleGetOcpiEndpoints.bind(this), +// [ServerAction.OCPI_ENDPOINT]: OCPIEndpointService.handleGetOcpiEndpoint.bind(this), +// [ServerAction.OICP_ENDPOINTS]: OICPEndpointService.handleGetOicpEndpoints.bind(this), +// [ServerAction.OICP_ENDPOINT]: OICPEndpointService.handleGetOicpEndpoint.bind(this), +// [ServerAction.INTEGRATION_CONNECTIONS]: ConnectionService.handleGetConnections.bind(this), +// [ServerAction.INTEGRATION_CONNECTION]: ConnectionService.handleGetConnection.bind(this), +// [ServerAction.PING]: (action: ServerAction, req: Request, res: Response, next: NextFunction) => { +// res.sendStatus(StatusCodes.OK); +// }, +// }); +// break; + +// // Update +// case 'PUT': +// // Register REST actions +// this.registerJsonActionsPaths({ +// [ServerAction.TENANT_UPDATE]: TenantService.handleUpdateTenant.bind(this), +// [ServerAction.SITE_UPDATE]: SiteService.handleUpdateSite.bind(this), +// [ServerAction.SITE_AREA_UPDATE]: SiteAreaService.handleUpdateSiteArea.bind(this), +// [ServerAction.COMPANY_UPDATE]: CompanyService.handleUpdateCompany.bind(this), +// [ServerAction.ASSET_UPDATE]: AssetService.handleUpdateAsset.bind(this), +// [ServerAction.SITE_USER_ADMIN]: SiteService.handleUpdateSiteUserAdmin.bind(this), +// [ServerAction.SITE_OWNER]: SiteService.handleUpdateSiteOwner.bind(this), +// [ServerAction.TRANSACTION_SOFT_STOP]: TransactionService.handleTransactionSoftStop.bind(this), +// [ServerAction.ASSIGN_TRANSACTIONS_TO_USER]: TransactionService.handleAssignTransactionsToUser.bind(this), +// [ServerAction.SETTING_UPDATE]: SettingService.handleUpdateSetting.bind(this), +// [ServerAction.OCPI_ENDPOINT_UPDATE]: OCPIEndpointService.handleUpdateOcpiEndpoint.bind(this), +// [ServerAction.OCPI_ENDPOINT_REGISTER]: OCPIEndpointService.handleRegisterOcpiEndpoint.bind(this), +// [ServerAction.OCPI_ENDPOINT_UNREGISTER]: OCPIEndpointService.handleUnregisterOcpiEndpoint.bind(this), +// [ServerAction.OICP_ENDPOINT_UPDATE]: OICPEndpointService.handleUpdateOicpEndpoint.bind(this), +// [ServerAction.OICP_ENDPOINT_REGISTER]: OICPEndpointService.handleRegisterOicpEndpoint.bind(this), +// [ServerAction.OICP_ENDPOINT_UNREGISTER]: OICPEndpointService.handleUnregisterOicpEndpoint.bind(this), +// [ServerAction.SYNCHRONIZE_CAR_CATALOGS]: CarService.handleSynchronizeCarCatalogs.bind(this), +// [ServerAction.CAR_UPDATE]: CarService.handleUpdateCar.bind(this), +// [ServerAction.TAG_UPDATE]: TagService.handleUpdateTag.bind(this), +// [ServerAction.BILLING_CHARGE_INVOICE]: BillingService.handleBillingChargeInvoice.bind(this), +// [ServerAction.REGISTRATION_TOKEN_UPDATE]: RegistrationTokenService.handleUpdateRegistrationToken.bind(this), +// }); +// break; + +// // Delete +// case 'DELETE': +// // Register REST actions +// this.registerJsonActionsPaths({ +// [ServerAction.TENANT_DELETE]: TenantService.handleDeleteTenant.bind(this), +// [ServerAction.REGISTRATION_TOKEN_DELETE]: RegistrationTokenService.handleDeleteRegistrationToken.bind(this), +// [ServerAction.REGISTRATION_TOKEN_REVOKE]: RegistrationTokenService.handleRevokeRegistrationToken.bind(this), +// [ServerAction.SITE_DELETE]: SiteService.handleDeleteSite.bind(this), +// [ServerAction.SITE_AREA_DELETE]: SiteAreaService.handleDeleteSiteArea.bind(this), +// [ServerAction.COMPANY_DELETE]: CompanyService.handleDeleteCompany.bind(this), +// [ServerAction.ASSET_DELETE]: AssetService.handleDeleteAsset.bind(this), +// [ServerAction.TRANSACTION_DELETE]: TransactionService.handleDeleteTransaction.bind(this), +// [ServerAction.TRANSACTIONS_DELETE]: TransactionService.handleDeleteTransactions.bind(this), +// [ServerAction.INTEGRATION_CONNECTION_DELETE]: ConnectionService.handleDeleteConnection.bind(this), +// [ServerAction.SETTING_DELETE]: SettingService.handleDeleteSetting.bind(this), +// [ServerAction.OCPI_ENDPOINT_DELETE]: OCPIEndpointService.handleDeleteOcpiEndpoint.bind(this), +// [ServerAction.OICP_ENDPOINT_DELETE]: OICPEndpointService.handleDeleteOicpEndpoint.bind(this), +// [ServerAction.CAR_DELETE]: CarService.handleDeleteCar.bind(this), +// [ServerAction.TAG_DELETE]: TagService.handleDeleteTag.bind(this), +// [ServerAction.TAGS_DELETE]: TagService.handleDeleteTags.bind(this), +// [ServerAction.BILLING_DELETE_PAYMENT_METHOD]: BillingService.handleBillingDeletePaymentMethod.bind(this), +// }); +// break; +// } +// } + +// public static getInstanceFromHTTPVerb(method: string): RequestMapper { +// if (!RequestMapper.instances.has(method)) { +// RequestMapper.instances.set(method, new RequestMapper(method)); +// } +// return RequestMapper.instances.get(method); +// } + +// public registerOneActionManyPaths(action: (action: ServerAction, req: Request, res: Response, next: NextFunction) => void|Promise, ...paths: ServerAction[]) { +// const index = this.actions.push(action) - 1; +// for (const path of paths) { +// this.paths.set(path, index); +// } +// } + +// public registerJsonActionsPaths(dict: { [key in ServerAction]?: (action: ServerAction, req: Request, res: Response, next: NextFunction) => void|Promise; }) { +// for (const key in dict) { +// this.registerOneActionManyPaths(dict[key], key as ServerAction); +// } +// } + +// public getActionFromPath(path: string): (action: ServerAction, req: Request, res: Response, next: NextFunction) => void|Promise { +// if (!this.paths.has(path)) { +// return UtilsService.handleUnknownAction.bind(this); +// } +// return this.actions[this.paths.get(path)]; +// } +// } + +// export default class CentralRestServerService { +// // Util Service +// public static async restServiceUtil(req: Request, res: Response, next: NextFunction): Promise { +// try { +// // Parse the action +// const action = req.params.action as ServerAction; +// // Check Context +// switch (req.method) { +// // Create Request +// case 'GET': +// // Check Context +// switch (action) { +// // Ping +// case ServerAction.PING: +// res.sendStatus(StatusCodes.OK); +// break; +// case ServerAction.CAR_CATALOG_IMAGE: +// await CarService.handleGetCarCatalogImage(action, req, res, next); +// break; +// case ServerAction.ASSET_IMAGE: +// await AssetService.handleGetAssetImage(action, req, res, next); +// break; +// case ServerAction.COMPANY_LOGO: +// await CompanyService.handleGetCompanyLogo(action, req, res, next); +// break; +// case ServerAction.SITE_IMAGE: +// await SiteService.handleGetSiteImage(action, req, res, next); +// break; +// case ServerAction.SITE_AREA_IMAGE: +// await SiteAreaService.handleGetSiteAreaImage(action, req, res, next); +// break; +// case ServerAction.TENANT_LOGO: +// await TenantService.handleGetTenantLogo(action, req, res, next); +// break; +// default: +// // Delegate +// await UtilsService.handleUnknownAction(action, req, res, next); +// } +// break; + +// case 'POST': +// // Check Context +// switch (action) { +// // Ping +// case ServerAction.BILLING_WEB_HOOK: +// await BillingService.handleBillingWebHook(action, req, res, next); +// // Res.sendStatus(StatusCodes.OK); +// break; +// default: +// // Delegate +// await UtilsService.handleUnknownAction(action, req, res, next); +// } +// break; +// } +// } catch (error) { +// next(error); +// } +// } + +// public static async restServiceSecured(req: Request, res: Response, next: NextFunction): Promise { +// // Parse the action +// const action = req.params.action as ServerAction; +// // Check if User has been updated and require new login +// if ((await SessionHashService.areTokenUserAndTenantStillValid(req, res, next))) { +// return; +// } +// // Check HTTP Verbs +// if (!['POST', 'GET', 'PUT', 'DELETE'].includes(req.method)) { +// await Logging.logActionExceptionMessageAndSendResponse( +// null, new Error(`Unsupported request method ${req.method}`), req, res, next); +// return; +// } +// try { +// // Get the action +// const handleRequest = RequestMapper.getInstanceFromHTTPVerb(req.method).getActionFromPath(action); +// // Execute +// await handleRequest(action, req, res, next); +// } catch (error) { +// next(error); +// } +// } +// } diff --git a/src/server/rest/v1/service/AuthorizationService.ts b/src/server/rest/v1/service/AuthorizationService.ts index 03ee48127e..357b067947 100644 --- a/src/server/rest/v1/service/AuthorizationService.ts +++ b/src/server/rest/v1/service/AuthorizationService.ts @@ -524,7 +524,7 @@ export default class AuthorizationService { projectFields: [ 'id', 'inactive', 'public', 'chargingStationURL', 'issuer', 'maximumPower', 'excludeFromSmartCharging', 'lastReboot', 'siteAreaID', 'siteArea.id', 'siteArea.name', 'siteArea.smartCharging', 'siteArea.siteID', - 'siteArea.site.id', 'siteArea.site.name', 'siteID', 'voltage', 'coordinates', 'forceInactive', 'manualConfiguration', 'firmwareUpdateStatus', + 'site.id', 'site.name', 'siteID', 'voltage', 'coordinates', 'forceInactive', 'manualConfiguration', 'firmwareUpdateStatus', 'capabilities', 'endpoint', 'chargePointVendor', 'chargePointModel', 'ocppVersion', 'ocppProtocol', 'lastSeen', 'firmwareVersion', 'currentIPAddress', 'ocppStandardParameters', 'ocppVendorParameters', 'connectors', 'chargePoints', 'createdOn', 'chargeBoxSerialNumber', 'chargePointSerialNumber', 'powerLimitUnit' diff --git a/src/server/rest/v1/service/ChargingStationService.ts b/src/server/rest/v1/service/ChargingStationService.ts index a2f7ca87d0..f489fbbd5d 100644 --- a/src/server/rest/v1/service/ChargingStationService.ts +++ b/src/server/rest/v1/service/ChargingStationService.ts @@ -261,8 +261,9 @@ export default class ChargingStationService { action: action }); } - chargingStation.siteAreaID = siteArea.id; + chargingStation.companyID = siteArea.site?.companyID; chargingStation.siteID = siteArea.siteID; + chargingStation.siteAreaID = siteArea.id; // Check if number of phases corresponds to the site area one for (const connector of chargingStation.connectors) { const numberOfConnectedPhase = Utils.getNumberOfConnectedPhases(chargingStation, null, connector.connectorId); @@ -283,8 +284,9 @@ export default class ChargingStationService { } } else { delete chargingStation.excludeFromSmartCharging; - chargingStation.siteAreaID = null; + chargingStation.companyID = null; chargingStation.siteID = null; + chargingStation.siteAreaID = null; } if (filteredRequest.coordinates && filteredRequest.coordinates.length === 2) { chargingStation.coordinates = [ @@ -325,7 +327,6 @@ export default class ChargingStationService { 'chargingStationURL': chargingStation.chargingStationURL } }); - // Ok res.json(Constants.REST_RESPONSE_SUCCESS); next(); } @@ -475,7 +476,6 @@ export default class ChargingStationService { message: `The charger's power limit has been successfully set to ${filteredRequest.ampLimitValue}A`, detailedMessages: { result } }); - // Ok res.json({ status: result.status }); next(); } @@ -588,7 +588,6 @@ export default class ChargingStationService { await LockingManager.release(siteAreaLock); } } - // Ok res.json(Constants.REST_RESPONSE_SUCCESS); next(); } @@ -705,7 +704,6 @@ export default class ChargingStationService { detailedMessages: { error: error.stack } }); } - // Ok res.json(Constants.REST_RESPONSE_SUCCESS); next(); } @@ -763,7 +761,6 @@ export default class ChargingStationService { if (filteredRequest.forceUpdateOCPPParamsFromTemplate) { result = await OCPPUtils.updateChargingStationOcppParametersWithTemplate(req.tenant, chargingStation); } - // Ok res.json(result); next(); } @@ -865,11 +862,10 @@ export default class ChargingStationService { } } } - // Remove Site Area - chargingStation.siteArea = null; - chargingStation.siteAreaID = null; - // Remove Site + // Remove Org + chargingStation.companyID = null; chargingStation.siteID = null; + chargingStation.siteAreaID = null; // Set as deleted chargingStation.deleted = true; // Check if charging station has had transactions @@ -882,7 +878,6 @@ export default class ChargingStationService { // Delete physically await ChargingStationStorage.deleteChargingStation(req.user.tenantID, chargingStation.id); } - // Log await Logging.logSecurityInfo({ tenantID: req.user.tenantID, user: req.user, module: MODULE_NAME, method: 'handleDeleteChargingStation', @@ -890,7 +885,6 @@ export default class ChargingStationService { action: action, detailedMessages: { chargingStation } }); - // Ok res.json(Constants.REST_RESPONSE_SUCCESS); next(); } @@ -950,7 +944,7 @@ export default class ChargingStationService { // Get OCPP Params const dataToExport = ChargingStationService.convertOCPPParamsToCSV({ params: ocppParameters.result, - siteName: chargingStation.siteArea.site.name, + siteName: chargingStation.site.name, siteAreaName: chargingStation.siteArea.name, chargingStationName: chargingStation.id }, writeHeader); @@ -1323,7 +1317,6 @@ export default class ChargingStationService { } // Check await smartCharging.checkConnection(); - // Ok res.json(Constants.REST_RESPONSE_SUCCESS); next(); } @@ -1357,7 +1350,7 @@ export default class ChargingStationService { projectFields = [ 'id', 'inactive', 'connectorsStatus', 'connectorsConsumption', 'public', 'firmwareVersion', 'chargePointVendor', 'chargePointModel', 'ocppVersion', 'ocppProtocol', 'lastSeen', 'firmwareUpdateStatus', 'coordinates', 'issuer', 'voltage', 'distanceMeters', - 'siteAreaID', 'siteArea.id', 'siteArea.name', 'siteArea.siteID', 'siteArea.site.name', 'siteArea.address', 'siteID', 'maximumPower', 'powerLimitUnit', + 'siteAreaID', 'siteArea.id', 'siteArea.name', 'siteArea.siteID', 'site.name', 'siteArea.address', 'siteID', 'maximumPower', 'powerLimitUnit', 'chargePointModel', 'chargePointSerialNumber', 'chargeBoxSerialNumber', 'connectors.connectorId', 'connectors.status', 'connectors.type', 'connectors.power', 'connectors.errorCode', 'connectors.currentTotalConsumptionWh', 'connectors.currentInstantWatts', 'connectors.currentStateOfCharge', 'connectors.info', 'connectors.currentTransactionID', 'connectors.currentTotalInactivitySecs', 'connectors.currentTagID', 'chargePoints', 'lastReboot', 'createdOn', @@ -1385,12 +1378,14 @@ export default class ChargingStationService { search: filteredRequest.Search, withNoSiteArea: filteredRequest.WithNoSiteArea, withSite: filteredRequest.WithSite, + withSiteArea: filteredRequest.WithSiteArea, chargingStationIDs: filteredRequest.ChargingStationID ? filteredRequest.ChargingStationID.split('|') : null, connectorStatuses: filteredRequest.ConnectorStatus ? filteredRequest.ConnectorStatus.split('|') : null, connectorTypes: filteredRequest.ConnectorType ? filteredRequest.ConnectorType.split('|') : null, issuer: filteredRequest.Issuer, siteIDs: siteIDs, siteAreaIDs: filteredRequest.SiteAreaID ? filteredRequest.SiteAreaID.split('|') : null, + companyIDs: filteredRequest.CompanyID ? filteredRequest.CompanyID.split('|') : null, includeDeleted: filteredRequest.IncludeDeleted, locCoordinates: filteredRequest.LocCoordinates, locMaxDistanceMeters: filteredRequest.LocMaxDistanceMeters, @@ -1700,7 +1695,6 @@ export default class ChargingStationService { }); break; } - // Ok? if (result) { // OCPP Command with status if (Utils.objectHasProperty(result, 'status') && ![OCPPStatus.ACCEPTED, OCPPUnlockStatus.UNLOCKED].includes(result.status)) { @@ -1799,8 +1793,6 @@ export default class ChargingStationService { }); } // Apply & Save charging plan - const chargingProfileID = await OCPPUtils.setAndSaveChargingProfile(req.tenant, filteredRequest); - // Ok - return chargingProfileID; + return OCPPUtils.setAndSaveChargingProfile(req.tenant, filteredRequest); } } diff --git a/src/server/rest/v1/service/LoggingService.ts b/src/server/rest/v1/service/LoggingService.ts index b4e728112f..fa7a4bda92 100644 --- a/src/server/rest/v1/service/LoggingService.ts +++ b/src/server/rest/v1/service/LoggingService.ts @@ -106,7 +106,7 @@ export default class LoggingService { if (Utils.isComponentActiveFromToken(req.user, TenantComponents.ORGANIZATION) && Authorizations.isSiteAdmin(req.user)) { // Optimization: Retrieve Charging Stations to get the logs only for the Site Admin user const chargingStations = await ChargingStationStorage.getChargingStations(req.user.tenantID, - { siteIDs: req.user.sitesAdmin }, Constants.DB_PARAMS_MAX_LIMIT); + { siteIDs: req.user.sitesAdmin, withSiteArea: true }, Constants.DB_PARAMS_MAX_LIMIT); // Check if Charging Station is already filtered if (chargingStations.count === 0) { filteredRequest.Source = ''; diff --git a/src/server/rest/v1/service/OCPIEndpointService.ts b/src/server/rest/v1/service/OCPIEndpointService.ts index 5eddda4e13..10854db2a4 100644 --- a/src/server/rest/v1/service/OCPIEndpointService.ts +++ b/src/server/rest/v1/service/OCPIEndpointService.ts @@ -44,11 +44,11 @@ export default class OCPIEndpointService { }); } // Get - const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpoint(req.user.tenantID, filteredRequest.ID); + const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpoint(req.tenant, filteredRequest.ID); UtilsService.assertObjectExists(action, ocpiEndpoint, `OCPIEndpoint ID '${filteredRequest.ID}' does not exist`, MODULE_NAME, 'handleDeleteOcpiEndpoint', req.user); // Delete - await OCPIEndpointStorage.deleteOcpiEndpoint(req.user.tenantID, ocpiEndpoint.id); + await OCPIEndpointStorage.deleteOcpiEndpoint(req.tenant, ocpiEndpoint.id); // Log await Logging.logSecurityInfo({ tenantID: req.user.tenantID, @@ -80,7 +80,7 @@ export default class OCPIEndpointService { }); } // Get it - const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpoint(req.user.tenantID, endpointID, + const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpoint(req.tenant, endpointID, [ 'id', 'name', 'role', 'baseUrl', 'countryCode', 'partyId', 'version', 'status', 'patchJobStatus', 'localToken', 'token', 'patchJobResult.successNbr', 'patchJobResult.failureNbr', 'patchJobResult.totalNbr' @@ -112,7 +112,7 @@ export default class OCPIEndpointService { // Filter const filteredRequest = OCPIEndpointSecurity.filterOcpiEndpointsRequest(req.query); // Get all ocpiendpoints - const ocpiEndpoints = await OCPIEndpointStorage.getOcpiEndpoints(req.user.tenantID, + const ocpiEndpoints = await OCPIEndpointStorage.getOcpiEndpoints(req.tenant, { 'search': filteredRequest.Search }, { @@ -154,7 +154,7 @@ export default class OCPIEndpointService { createdOn: new Date(), status: OCPIRegistrationStatus.NEW } as OCPIEndpoint; - const endpointID = await OCPIEndpointStorage.saveOcpiEndpoint(req.user.tenantID, ocpiEndpoint); + const endpointID = await OCPIEndpointStorage.saveOcpiEndpoint(req.tenant, ocpiEndpoint); // Log await Logging.logSecurityInfo({ tenantID: req.user.tenantID, @@ -187,14 +187,14 @@ export default class OCPIEndpointService { }); } // Get OcpiEndpoint - const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpoint(req.user.tenantID, filteredRequest.id); + const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpoint(req.tenant, filteredRequest.id); UtilsService.assertObjectExists(action, ocpiEndpoint, `OCPI Endpoint ID '${filteredRequest.id}' does not exist`, MODULE_NAME, 'handleUpdateOcpiEndpoint', req.user); // Update timestamp ocpiEndpoint.lastChangedBy = { 'id': req.user.id }; ocpiEndpoint.lastChangedOn = new Date(); // Update OcpiEndpoint - await OCPIEndpointStorage.saveOcpiEndpoint(req.user.tenantID, { ...ocpiEndpoint, ...filteredRequest }); + await OCPIEndpointStorage.saveOcpiEndpoint(req.tenant, { ...ocpiEndpoint, ...filteredRequest }); // Log await Logging.logSecurityInfo({ tenantID: req.user.tenantID, @@ -225,9 +225,8 @@ export default class OCPIEndpointService { const filteredRequest = OCPIEndpointSecurity.filterOcpiEndpointPingRequest(req.body); // Check Mandatory fields UtilsService.checkIfOCPIEndpointValid(filteredRequest, req); - const tenant = await TenantStorage.getTenant(req.user.tenantID); // Build OCPI Client - const ocpiClient = await OCPIClientFactory.getOcpiClient(tenant, filteredRequest); + const ocpiClient = await OCPIClientFactory.getOcpiClient(req.tenant, filteredRequest); // Try to ping const pingResult = await ocpiClient.ping(); // Check ping result @@ -272,7 +271,7 @@ export default class OCPIEndpointService { const filteredRequest = OCPIEndpointSecurity.filterOcpiEndpointTriggerJobRequest(req.body); UtilsService.assertIdIsProvided(action, filteredRequest.id, MODULE_NAME, 'handlePullLocationsEndpoint', req.user); // Get ocpiEndpoint - const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpoint(req.user.tenantID, filteredRequest.id); + const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpoint(req.tenant, filteredRequest.id); UtilsService.assertObjectExists(action, ocpiEndpoint, `OCPI Endpoint ID '${filteredRequest.id}' does not exist`, MODULE_NAME, 'handlePullLocationsEndpoint', req.user); // Get the lock @@ -326,7 +325,7 @@ export default class OCPIEndpointService { const filteredRequest = OCPIEndpointSecurity.filterOcpiEndpointTriggerJobRequest(req.body); UtilsService.assertIdIsProvided(action, filteredRequest.id, MODULE_NAME, 'handlePullSessionsEndpoint', req.user); // Get ocpiEndpoint - const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpoint(req.user.tenantID, filteredRequest.id); + const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpoint(req.tenant, filteredRequest.id); UtilsService.assertObjectExists(action, ocpiEndpoint, `OCPI Endpoint ID '${filteredRequest.id}' does not exist`, MODULE_NAME, 'handlePullSessionsEndpoint', req.user); const tenant = await TenantStorage.getTenant(req.user.tenantID); @@ -380,7 +379,7 @@ export default class OCPIEndpointService { const filteredRequest = OCPIEndpointSecurity.filterOcpiEndpointTriggerJobRequest(req.body); UtilsService.assertIdIsProvided(action, filteredRequest.id, MODULE_NAME, 'handlePullTokensEndpoint', req.user); // Get ocpiEndpoint - const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpoint(req.user.tenantID, filteredRequest.id); + const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpoint(req.tenant, filteredRequest.id); UtilsService.assertObjectExists(action, ocpiEndpoint, `OCPI Endpoint ID '${filteredRequest.id}' does not exist`, MODULE_NAME, 'handlePullTokensEndpoint', req.user); const tenant = await TenantStorage.getTenant(req.user.tenantID); @@ -434,7 +433,7 @@ export default class OCPIEndpointService { const filteredRequest = OCPIEndpointSecurity.filterOcpiEndpointTriggerJobRequest(req.body); UtilsService.assertIdIsProvided(action, filteredRequest.id, MODULE_NAME, 'handlePullCdrsEndpoint', req.user); // Get ocpiEndpoint - const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpoint(req.user.tenantID, filteredRequest.id); + const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpoint(req.tenant, filteredRequest.id); UtilsService.assertObjectExists(action, ocpiEndpoint, `OCPI Endpoint ID '${filteredRequest.id}' does not exist`, MODULE_NAME, 'handlePullCdrsEndpoint', req.user); const tenant = await TenantStorage.getTenant(req.user.tenantID); @@ -488,7 +487,7 @@ export default class OCPIEndpointService { const filteredRequest = OCPIEndpointSecurity.filterOcpiCheckCdrsRequest(req.body); UtilsService.assertIdIsProvided(action, filteredRequest.id, MODULE_NAME, 'handleCheckCdrsEndpoint', req.user); // Get ocpiEndpoint - const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpoint(req.user.tenantID, filteredRequest.id); + const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpoint(req.tenant, filteredRequest.id); UtilsService.assertObjectExists(action, ocpiEndpoint, `OCPI Endpoint ID '${filteredRequest.id}' does not exist`, MODULE_NAME, 'handleCheckCdrsEndpoint', req.user); const tenant = await TenantStorage.getTenant(req.user.tenantID); @@ -542,7 +541,7 @@ export default class OCPIEndpointService { const filteredRequest = OCPIEndpointSecurity.filterOcpiCheckSessionsRequest(req.body); UtilsService.assertIdIsProvided(action, filteredRequest.id, MODULE_NAME, 'handleCheckSessionsEndpoint', req.user); // Get ocpiEndpoint - const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpoint(req.user.tenantID, filteredRequest.id); + const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpoint(req.tenant, filteredRequest.id); UtilsService.assertObjectExists(action, ocpiEndpoint, `OCPI Endpoint ID '${filteredRequest.id}' does not exist`, MODULE_NAME, 'handleCheckSessionsEndpoint', req.user); const tenant = await TenantStorage.getTenant(req.user.tenantID); @@ -596,7 +595,7 @@ export default class OCPIEndpointService { const filteredRequest = OCPIEndpointSecurity.filterOcpiCheckLocationsRequest(req.body); UtilsService.assertIdIsProvided(action, filteredRequest.id, MODULE_NAME, 'handleCheckSessionsOcpiEndpoint', req.user); // Get ocpiEndpoint - const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpoint(req.user.tenantID, filteredRequest.id); + const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpoint(req.tenant, filteredRequest.id); UtilsService.assertObjectExists(action, ocpiEndpoint, `OCPI Endpoint ID '${filteredRequest.id}' does not exist`, MODULE_NAME, 'handleCheckLocationsEndpoint', req.user); const tenant = await TenantStorage.getTenant(req.user.tenantID); @@ -650,7 +649,7 @@ export default class OCPIEndpointService { const filteredRequest = OCPIEndpointSecurity.filterOcpiEndpointSendEVSEStatusesRequest(req.body); UtilsService.assertIdIsProvided(action, filteredRequest.id, MODULE_NAME, 'handlePushEVSEStatusesOcpiEndpoint', req.user); // Get ocpiEndpoint - const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpoint(req.user.tenantID, filteredRequest.id); + const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpoint(req.tenant, filteredRequest.id); UtilsService.assertObjectExists(action, ocpiEndpoint, `OCPI Endpoint ID '${filteredRequest.id}' does not exist`, MODULE_NAME, 'handlePushEVSEStatusesOcpiEndpoint', req.user); const tenant = await TenantStorage.getTenant(req.user.tenantID); @@ -704,7 +703,7 @@ export default class OCPIEndpointService { const filteredRequest = OCPIEndpointSecurity.filterOcpiEndpointSendTokensRequest(req.body); UtilsService.assertIdIsProvided(action, filteredRequest.id, MODULE_NAME, 'handlePushTokensOcpiEndpoint', req.user); // Get ocpiEndpoint - const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpoint(req.user.tenantID, filteredRequest.id); + const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpoint(req.tenant, filteredRequest.id); UtilsService.assertObjectExists(action, ocpiEndpoint, `OCPI Endpoint ID '${filteredRequest.id}' does not exist`, MODULE_NAME, 'handlePushTokensOcpiEndpoint', req.user); // Get the lock @@ -758,7 +757,7 @@ export default class OCPIEndpointService { const filteredRequest = OCPIEndpointSecurity.filterOcpiEndpointRegisterRequest(req.body); UtilsService.assertIdIsProvided(action, filteredRequest.id, MODULE_NAME, 'handleUnregisterOcpiEndpoint', req.user); // Get OcpiEndpoint - const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpoint(req.user.tenantID, filteredRequest.id); + const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpoint(req.tenant, filteredRequest.id); UtilsService.assertObjectExists(action, ocpiEndpoint, `OCPI Endpoint ID '${filteredRequest.id}' does not exist`, MODULE_NAME, 'handleUnregisterOcpiEndpoint', req.user); const tenant = await TenantStorage.getTenant(req.user.tenantID); @@ -808,7 +807,7 @@ export default class OCPIEndpointService { const filteredRequest = OCPIEndpointSecurity.filterOcpiEndpointRegisterRequest(req.body); UtilsService.assertIdIsProvided(action, filteredRequest.id, MODULE_NAME, 'handleRegisterOcpiEndpoint', req.user); // Get OcpiEndpoint - const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpoint(req.user.tenantID, filteredRequest.id); + const ocpiEndpoint = await OCPIEndpointStorage.getOcpiEndpoint(req.tenant, filteredRequest.id); UtilsService.assertObjectExists(action, ocpiEndpoint, `OCPI Endpoint ID '${filteredRequest.id}' does not exist`, MODULE_NAME, 'handleRegisterOcpiEndpoint', req.user); const tenant = await TenantStorage.getTenant(req.user.tenantID); diff --git a/src/server/rest/v1/service/SiteAreaService.ts b/src/server/rest/v1/service/SiteAreaService.ts index 94f490880c..06794fd769 100644 --- a/src/server/rest/v1/service/SiteAreaService.ts +++ b/src/server/rest/v1/service/SiteAreaService.ts @@ -69,7 +69,7 @@ export default class SiteAreaService { // Check and Get Site Area const authAction = action === ServerAction.ADD_CHARGING_STATIONS_TO_SITE_AREA ? Action.ASSIGN_CHARGING_STATIONS_TO_SITE_AREA : Action.UNASSIGN_CHARGING_STATIONS_TO_SITE_AREA; const siteArea = await UtilsService.checkAndGetSiteAreaAuthorization( - req.tenant, req.user, filteredRequest.siteAreaID, authAction, action); + req.tenant, req.user, filteredRequest.siteAreaID, authAction, action, { withSite: true }); // Check and Get Charging Stations const chargingStations = await UtilsService.checkSiteAreaChargingStationsAuthorization( req.tenant, req.user, siteArea, filteredRequest.chargingStationIDs, action); @@ -206,6 +206,7 @@ export default class SiteAreaService { locCoordinates: filteredRequest.LocCoordinates, locMaxDistanceMeters: filteredRequest.LocMaxDistanceMeters, siteIDs: (filteredRequest.SiteID ? filteredRequest.SiteID.split('|') : null), + companyIDs: (filteredRequest.CompanyID ? filteredRequest.CompanyID.split('|') : null), ...authorizationSiteAreasFilter.filters }, { diff --git a/src/server/rest/v1/service/TransactionService.ts b/src/server/rest/v1/service/TransactionService.ts index cb808c9980..3762ac380b 100644 --- a/src/server/rest/v1/service/TransactionService.ts +++ b/src/server/rest/v1/service/TransactionService.ts @@ -45,12 +45,12 @@ export default class TransactionService { public static async handleGetTransactions(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise { res.json(await TransactionService.getTransactions(req, action, {}, [ - 'id', 'chargeBoxID', 'timestamp', 'issuer', 'stateOfCharge', 'timezone', 'connectorId', 'meterStart', 'siteAreaID', 'siteID', + 'id', 'chargeBoxID', 'timestamp', 'issuer', 'stateOfCharge', 'timezone', 'connectorId', 'meterStart', 'siteAreaID', 'siteID', 'companyID', 'currentTotalDurationSecs', 'currentTotalInactivitySecs', 'currentInstantWatts', 'currentTotalConsumptionWh', 'currentStateOfCharge', 'currentCumulatedPrice', 'currentInactivityStatus', 'roundedPrice', 'price', 'priceUnit', 'tagID', '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', + 'billingData.stop.invoiceNumber', 'stop.reason', 'ocpi', 'ocpiWithCdr', 'tagID', 'stop.tagID', 'site.name', 'siteArea.name', 'company.name' ])); next(); } @@ -522,7 +522,7 @@ export default class TransactionService { UtilsService.assertIdIsProvided(action, filteredRequest.TransactionId, MODULE_NAME, 'handleGetConsumptionFromTransaction', req.user); let projectFields = [ - 'id', 'chargeBoxID', 'timestamp', 'issuer', 'stateOfCharge', 'timezone', 'connectorId', 'meterStart', 'siteAreaID', 'siteID', + 'id', 'chargeBoxID', 'timestamp', 'issuer', 'stateOfCharge', 'timezone', 'connectorId', 'meterStart', 'siteAreaID', 'siteID', 'companyID', 'currentTotalDurationSecs', 'currentTotalInactivitySecs', 'currentInstantWatts', 'currentTotalConsumptionWh', 'currentStateOfCharge', 'currentInactivityStatus', 'stop.roundedPrice', 'stop.price', 'stop.priceUnit', 'stop.inactivityStatus', 'stop.stateOfCharge', 'stop.timestamp', 'stop.totalConsumptionWh', 'stop.totalDurationSecs', 'stop.totalInactivitySecs', 'stop.extraInactivitySecs', 'stop.pricingSource', 'stop.reason', @@ -624,7 +624,7 @@ export default class TransactionService { 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', + 'id', 'chargeBoxID', 'timestamp', 'issuer', 'stateOfCharge', 'tagID', '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', @@ -665,10 +665,10 @@ export default class TransactionService { public static async handleGetChargingStationTransactions(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise { // Get transaction const transactions = await TransactionService.getTransactions(req, action, {}, [ - 'id', 'chargeBoxID', 'timestamp', 'issuer', 'stateOfCharge', 'timezone', 'connectorId', 'meterStart', 'siteAreaID', 'siteID', + 'id', 'chargeBoxID', 'timestamp', 'issuer', 'stateOfCharge', 'timezone', 'connectorId', 'meterStart', 'siteAreaID', 'siteID', 'companyID', 'currentTotalDurationSecs', 'currentTotalInactivitySecs', 'currentInstantWatts', 'currentTotalConsumptionWh', 'currentStateOfCharge', 'currentInactivityStatus', 'stop.roundedPrice', 'stop.price', 'stop.priceUnit', 'stop.inactivityStatus', 'stop.stateOfCharge', 'stop.timestamp', 'stop.totalConsumptionWh', - 'stop.totalDurationSecs', 'stop.totalInactivitySecs', 'stop.extraInactivitySecs', + 'stop.totalDurationSecs', 'stop.totalInactivitySecs', 'stop.extraInactivitySecs', 'site.name', 'siteArea.name', 'company.name', 'billingData.stop.invoiceNumber', 'stop.reason', 'ocpi', 'ocpiWithCdr', 'tagID', 'stop.tagID', ]); res.json(transactions); @@ -691,9 +691,9 @@ export default class TransactionService { public static async handleGetTransactionsActive(action: ServerAction, req: Request, res: Response, next: NextFunction): Promise { req.query.Status = 'active'; const transactions = await TransactionService.getTransactions(req, action, {}, [ - 'id', 'chargeBoxID', 'timestamp', 'issuer', 'stateOfCharge', 'timezone', 'connectorId', 'status', 'meterStart', 'siteAreaID', 'siteID', + 'id', 'chargeBoxID', 'timestamp', 'issuer', 'stateOfCharge', 'timezone', 'connectorId', 'status', 'meterStart', 'siteAreaID', 'siteID', 'companyID', 'currentTotalDurationSecs', 'currentTotalInactivitySecs', 'currentInstantWatts', 'currentTotalConsumptionWh', 'currentStateOfCharge', - 'currentCumulatedPrice', 'currentInactivityStatus', 'roundedPrice', 'price', 'priceUnit', 'tagID', + 'currentCumulatedPrice', 'currentInactivityStatus', 'roundedPrice', 'price', 'priceUnit', 'tagID', 'site.name', 'siteArea.name', 'company.name', ]); res.json(transactions); next(); @@ -703,9 +703,9 @@ export default class TransactionService { // Get transaction req.query.Status = 'completed'; const transactions = await TransactionService.getTransactions(req, action, {}, [ - 'id', 'chargeBoxID', 'timestamp', 'issuer', 'stateOfCharge', 'timezone', 'connectorId', 'meterStart', 'siteAreaID', 'siteID', + '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', + 'stop.totalDurationSecs', 'stop.totalInactivitySecs', 'stop.extraInactivitySecs', 'stop.meterStop', 'site.name', 'siteArea.name', 'company.name', 'billingData.stop.invoiceNumber', 'stop.reason', 'ocpi', 'ocpiWithCdr', 'tagID', 'stop.tagID', ]); res.json(transactions); @@ -721,8 +721,8 @@ export default class TransactionService { // Call req.query.Status = 'completed'; const transactions = await TransactionService.getTransactions(req, action, {}, [ - 'id', 'chargeBoxID', 'timestamp', 'issuer', 'stateOfCharge', 'timezone', 'connectorId', 'meterStart', 'siteAreaID', 'siteID', - 'refundData.reportId', 'refundData.refundedAt', 'refundData.status', + 'id', 'chargeBoxID', 'timestamp', 'issuer', 'stateOfCharge', 'timezone', 'connectorId', 'meterStart', 'siteAreaID', 'siteID', 'companyID', + 'refundData.reportId', 'refundData.refundedAt', 'refundData.status', 'site.name', 'siteArea.name', 'company.name', 'stop.roundedPrice', 'stop.price', 'stop.priceUnit', 'stop.inactivityStatus', 'stop.stateOfCharge', 'stop.timestamp', 'stop.totalConsumptionWh', 'stop.totalDurationSecs', 'stop.totalInactivitySecs', 'stop.extraInactivitySecs', 'billingData.stop.invoiceNumber', 'tagID', 'stop.tagID', 'stop.reason', @@ -788,9 +788,9 @@ export default class TransactionService { // Get transaction req.query.Status = 'completed'; return TransactionService.getTransactions(req, ServerAction.TRANSACTIONS_EXPORT, { withTag: true }, [ - 'id', 'chargeBoxID', 'timestamp', 'issuer', 'stateOfCharge', 'timezone', 'connectorId', 'meterStart', 'siteAreaID', 'siteID', + '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.totalDurationSecs', 'stop.totalInactivitySecs', 'stop.extraInactivitySecs', 'site.name', 'siteArea.name', 'company.name', 'billingData.stop.invoiceNumber', 'stop.reason', 'ocpi', 'ocpiWithCdr', 'tagID', 'stop.tagID', 'tag.description' ]); } @@ -805,8 +805,8 @@ export default class TransactionService { public static async getRefundedTransactionsToExport(req: Request): Promise> { req.query.Status = 'completed'; return await TransactionService.getTransactions(req, ServerAction.TRANSACTIONS_TO_REFUND_EXPORT, {}, [ - 'id', 'chargeBoxID', 'timestamp', 'issuer', 'stateOfCharge', 'timezone', 'connectorId', 'meterStart', 'siteAreaID', 'siteID', - 'refundData.reportId', 'refundData.refundedAt', 'refundData.status', + 'id', 'chargeBoxID', 'timestamp', 'issuer', 'stateOfCharge', 'timezone', 'connectorId', 'meterStart', 'siteAreaID', 'siteID', 'companyID', + 'refundData.reportId', 'refundData.refundedAt', 'refundData.status', 'site.name', 'siteArea.name', 'company.name', 'stop.roundedPrice', 'stop.price', 'stop.priceUnit', 'stop.inactivityStatus', 'stop.stateOfCharge', 'stop.timestamp', 'stop.totalConsumptionWh', 'stop.totalDurationSecs', 'stop.totalInactivitySecs', 'stop.extraInactivitySecs', 'billingData.stop.invoiceNumber', 'stop.reason', 'tagID', 'stop.tagID', @@ -860,7 +860,7 @@ export default class TransactionService { } let projectFields = [ 'id', 'chargeBoxID', 'timestamp', 'issuer', 'stateOfCharge', 'timezone', 'connectorId', - 'meterStart', 'siteAreaID', 'siteID', 'errorCode', 'uniqueId', 'stop.totalConsumptionWh', + 'meterStart', 'siteAreaID', 'siteID', 'companyID', 'errorCode', 'uniqueId', 'stop.totalConsumptionWh', 'stop.totalDurationSecs', 'stop.stateOfCharge' ]; // Check Users @@ -918,7 +918,10 @@ export default class TransactionService { const headerArray = [ 'id', 'chargingStationID', - 'connector', + 'connectorID', + 'companyName', + 'siteName', + 'siteAreaName', 'userID', 'user', 'tagID', @@ -942,6 +945,9 @@ export default class TransactionService { transaction.id, transaction.chargeBoxID, transaction.connectorId, + transaction.company?.name, + transaction.site?.name, + transaction.siteArea?.name, transaction.user ? Cypher.hash(transaction.user.id) : '', transaction.user ? Utils.buildUserFullName(transaction.user, false) : '', transaction.tagID, @@ -1106,7 +1112,10 @@ export default class TransactionService { userIDs: filteredRequest.UserID ? filteredRequest.UserID.split('|') : null, tagIDs: filteredRequest.TagID ? filteredRequest.TagID.split('|') : null, ownerID: Authorizations.isBasic(req.user) ? req.user.id : null, + withSite: filteredRequest.WithSite, + withCompany: filteredRequest.WithCompany, siteAreaIDs: filteredRequest.SiteAreaID ? filteredRequest.SiteAreaID.split('|') : null, + withSiteArea: filteredRequest.WithSiteArea, siteIDs: filteredRequest.SiteID ? Authorizations.getAuthorizedSiteAdminIDs(req.user, filteredRequest.SiteID.split('|')) : null, siteAdminIDs: Authorizations.getAuthorizedSiteAdminIDs(req.user), startDateTime: filteredRequest.StartDateTime ? filteredRequest.StartDateTime : null, diff --git a/src/server/rest/v1/service/security/SiteAreaSecurity.ts b/src/server/rest/v1/service/security/SiteAreaSecurity.ts index f98805c1bf..a9281ed141 100644 --- a/src/server/rest/v1/service/security/SiteAreaSecurity.ts +++ b/src/server/rest/v1/service/security/SiteAreaSecurity.ts @@ -47,7 +47,8 @@ export default class SiteAreaSecurity { WithSite: !request.WithSite ? false : UtilsSecurity.filterBoolean(request.WithSite), WithChargeBoxes: !request.WithChargeBoxes ? false : UtilsSecurity.filterBoolean(request.WithChargeBoxes), WithAvailableChargers: !request.WithAvailableChargers ? false : UtilsSecurity.filterBoolean(request.WithAvailableChargers), - SiteID: sanitize(request.SiteID) + SiteID: sanitize(request.SiteID), + CompanyID: sanitize(request.CompanyID) } as HttpSiteAreasRequest; if (Utils.objectHasProperty(request, 'Issuer')) { filteredRequest.Issuer = UtilsSecurity.filterBoolean(request.Issuer); diff --git a/src/storage/mongodb/ChargingStationStorage.ts b/src/storage/mongodb/ChargingStationStorage.ts index 3279616c44..bb5a8fd154 100644 --- a/src/storage/mongodb/ChargingStationStorage.ts +++ b/src/storage/mongodb/ChargingStationStorage.ts @@ -137,6 +137,7 @@ export default class ChargingStationStorage { const chargingStationsMDB = await ChargingStationStorage.getChargingStations(tenantID, { chargingStationIDs: [id], withSite: true, + withSiteArea: true, includeDeleted: params.includeDeleted, issuer: params.issuer, siteIDs: params.siteIDs, @@ -148,6 +149,7 @@ export default class ChargingStationStorage { projectFields?: string[]): Promise { const chargingStationsMDB = await ChargingStationStorage.getChargingStations(tenantID, { ocpiEvseID, + withSiteArea: true, }, Constants.DB_PARAMS_SINGLE_RECORD, projectFields); return chargingStationsMDB.count === 1 ? chargingStationsMDB.result[0] : null; } @@ -158,6 +160,7 @@ export default class ChargingStationStorage { const chargingStationsMDB = await ChargingStationStorage.getChargingStations(tenantID, { ocpiLocationID, ocpiEvseUid, + withSiteArea: true }, Constants.DB_PARAMS_SINGLE_RECORD, projectFields); return chargingStationsMDB.count === 1 ? chargingStationsMDB.result[0] : null; } @@ -166,6 +169,7 @@ export default class ChargingStationStorage { projectFields?: string[]): Promise { const chargingStationsMDB = await ChargingStationStorage.getChargingStations(tenantID, { oicpEvseID: oicpEvseID, + withSiteArea: true }, Constants.DB_PARAMS_SINGLE_RECORD, projectFields); return chargingStationsMDB.count === 1 ? chargingStationsMDB.result[0] : null; } @@ -173,9 +177,9 @@ export default class ChargingStationStorage { public static async getChargingStations(tenantID: string, params: { search?: string; chargingStationIDs?: string[]; chargingStationSerialNumbers?: string[]; siteAreaIDs?: string[]; withNoSiteArea?: boolean; - connectorStatuses?: string[]; connectorTypes?: string[]; statusChangedBefore?: Date; + connectorStatuses?: string[]; connectorTypes?: string[]; statusChangedBefore?: Date; withSiteArea?: boolean; ocpiEvseUid?: string; ocpiEvseID?: string; ocpiLocationID?: string; oicpEvseID?: string; - siteIDs?: string[]; withSite?: boolean; includeDeleted?: boolean; offlineSince?: Date; issuer?: boolean; + siteIDs?: string[]; companyIDs?: string[]; withSite?: boolean; includeDeleted?: boolean; offlineSince?: Date; issuer?: boolean; locCoordinates?: number[]; locMaxDistanceMeters?: number; public?: boolean; }, dbParams: DbParams, projectFields?: string[]): Promise> { @@ -314,6 +318,11 @@ export default class ChargingStationStorage { // Query by siteID filters.siteID = { $in: params.siteIDs.map((id) => DatabaseUtils.convertToObjectID(id)) }; } + // Check Company ID + if (!Utils.isEmptyArray(params.companyIDs)) { + // Query by siteID + filters.companyID = { $in: params.companyIDs.map((id) => DatabaseUtils.convertToObjectID(id)) }; + } // Date before provided if (params.statusChangedBefore && moment(params.statusChangedBefore).isValid()) { aggregation.push({ @@ -366,15 +375,17 @@ export default class ChargingStationStorage { asField: 'connectors.user', oneToOneCardinality: true, objectIDFields: ['createdBy', 'lastChangedBy'] }, { sort: dbParams.sort }); // Site Area - DatabaseUtils.pushSiteAreaLookupInAggregation({ - tenantID, aggregation: aggregation, localField: 'siteAreaID', foreignField: '_id', - asField: 'siteArea', oneToOneCardinality: true - }); + if (params.withSiteArea) { + DatabaseUtils.pushSiteAreaLookupInAggregation({ + tenantID, aggregation: aggregation, localField: 'siteAreaID', foreignField: '_id', + asField: 'siteArea', oneToOneCardinality: true + }); + } // Site - if (params.withSite && !params.withNoSiteArea) { + if (params.withSite) { DatabaseUtils.pushSiteLookupInAggregation({ - tenantID, aggregation: aggregation, localField: 'siteArea.siteID', foreignField: '_id', - asField: 'siteArea.site', oneToOneCardinality: true + tenantID, aggregation: aggregation, localField: 'siteID', foreignField: '_id', + asField: 'site', oneToOneCardinality: true }); } // Change ID @@ -389,7 +400,6 @@ export default class ChargingStationStorage { // Project DatabaseUtils.projectFields(aggregation, projectFields); // Reorder connector ID - // TODO: To remove when SiteID optimization will be implemented if (!Utils.containsGPSCoordinates(params.locCoordinates)) { aggregation.push({ $sort: dbParams.sort @@ -549,8 +559,9 @@ export default class ChargingStationStorage { templateHashOcppVendor: chargingStationToSave.templateHashOcppVendor, issuer: Utils.convertToBoolean(chargingStationToSave.issuer), public: Utils.convertToBoolean(chargingStationToSave.public), - siteAreaID: DatabaseUtils.convertToObjectID(chargingStationToSave.siteAreaID), + companyID: DatabaseUtils.convertToObjectID(chargingStationToSave.companyID), siteID: DatabaseUtils.convertToObjectID(chargingStationToSave.siteID), + siteAreaID: DatabaseUtils.convertToObjectID(chargingStationToSave.siteAreaID), chargePointSerialNumber: chargingStationToSave.chargePointSerialNumber, chargePointModel: chargingStationToSave.chargePointModel, chargeBoxSerialNumber: chargingStationToSave.chargeBoxSerialNumber, diff --git a/src/storage/mongodb/OCPIEndpointStorage.ts b/src/storage/mongodb/OCPIEndpointStorage.ts index c7a139bc0f..4503dd24da 100644 --- a/src/storage/mongodb/OCPIEndpointStorage.ts +++ b/src/storage/mongodb/OCPIEndpointStorage.ts @@ -9,28 +9,29 @@ import Logging from '../../utils/Logging'; import OCPIEndpoint from '../../types/ocpi/OCPIEndpoint'; import { OCPIRole } from '../../types/ocpi/OCPIRole'; import { ObjectID } from 'mongodb'; +import Tenant from '../../types/Tenant'; import Utils from '../../utils/Utils'; const MODULE_NAME = 'OCPIEndpointStorage'; export default class OCPIEndpointStorage { - static async getOcpiEndpoint(tenantID: string, id: string, projectFields?: string[]): Promise { + static async getOcpiEndpoint(tenant: Tenant, id: string, projectFields?: string[]): Promise { const endpointsMDB = await OCPIEndpointStorage.getOcpiEndpoints( - tenantID, { ocpiEndpointIDs: [id] }, Constants.DB_PARAMS_SINGLE_RECORD, projectFields); + tenant, { ocpiEndpointIDs: [id] }, Constants.DB_PARAMS_SINGLE_RECORD, projectFields); return endpointsMDB.count === 1 ? endpointsMDB.result[0] : null; } - static async getOcpiEndpointByLocalToken(tenantID: string, token: string, projectFields?: string[]): Promise { + static async getOcpiEndpointByLocalToken(tenant: Tenant, token: string, projectFields?: string[]): Promise { const endpointsMDB = await OCPIEndpointStorage.getOcpiEndpoints( - tenantID, { localToken: token }, Constants.DB_PARAMS_SINGLE_RECORD, projectFields); + tenant, { localToken: token }, Constants.DB_PARAMS_SINGLE_RECORD, projectFields); return endpointsMDB.count === 1 ? endpointsMDB.result[0] : null; } - static async saveOcpiEndpoint(tenantID: string, ocpiEndpointToSave: OCPIEndpoint): Promise { + static async saveOcpiEndpoint(tenant: Tenant, ocpiEndpointToSave: OCPIEndpoint): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'saveOcpiEndpoint'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'saveOcpiEndpoint'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Check if name is provided if (!ocpiEndpointToSave.name) { // Name must be provided! @@ -69,24 +70,24 @@ export default class OCPIEndpointStorage { // Add Last Changed/Created props DatabaseUtils.addLastChangedCreatedProps(ocpiEndpointMDB, ocpiEndpointToSave); // Modify - await global.database.getCollection(tenantID, 'ocpiendpoints').findOneAndUpdate( + await global.database.getCollection(tenant.id, 'ocpiendpoints').findOneAndUpdate( ocpiEndpointFilter, { $set: ocpiEndpointMDB }, { upsert: true, returnDocument: 'after' }); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'saveOcpiEndpoint', uniqueTimerID, ocpiEndpointMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'saveOcpiEndpoint', uniqueTimerID, ocpiEndpointMDB); // Create return ocpiEndpointFilter._id.toHexString(); } // Delegate - static async getOcpiEndpoints(tenantID: string, + static async getOcpiEndpoints(tenant: Tenant, params: { search?: string; role?: OCPIRole; ocpiEndpointIDs?: string[]; localToken?: string }, dbParams: DbParams, projectFields?: string[]): Promise> { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'getOcpiEndpoints'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'getOcpiEndpoints'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Clone before updating the values dbParams = Utils.cloneObject(dbParams); // Check Limit @@ -125,12 +126,12 @@ export default class OCPIEndpointStorage { aggregation.push({ $limit: Constants.DB_RECORD_COUNT_CEIL }); } // Count Records - const ocpiEndpointsCountMDB = await global.database.getCollection(tenantID, 'ocpiendpoints') + const ocpiEndpointsCountMDB = await global.database.getCollection(tenant.id, 'ocpiendpoints') .aggregate([...aggregation, { $count: 'count' }]) .toArray(); // Check if only the total count is requested if (dbParams.onlyRecordCount) { - await Logging.traceEnd(tenantID, MODULE_NAME, 'getOcpiEndpoints', uniqueTimerID, ocpiEndpointsCountMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'getOcpiEndpoints', uniqueTimerID, ocpiEndpointsCountMDB); return { count: (ocpiEndpointsCountMDB.length > 0 ? ocpiEndpointsCountMDB[0].count : 0), result: [] @@ -139,7 +140,7 @@ export default class OCPIEndpointStorage { // Remove the limit aggregation.pop(); // Add Created By / Last Changed By - DatabaseUtils.pushCreatedLastChangedInAggregation(tenantID, aggregation); + DatabaseUtils.pushCreatedLastChangedInAggregation(tenant.id, aggregation); // Handle the ID DatabaseUtils.pushRenameDatabaseID(aggregation); // Sort @@ -160,13 +161,13 @@ export default class OCPIEndpointStorage { // Project DatabaseUtils.projectFields(aggregation, projectFields); // Read DB - const ocpiEndpointsMDB = await global.database.getCollection(tenantID, 'ocpiendpoints') + const ocpiEndpointsMDB = await global.database.getCollection(tenant.id, 'ocpiendpoints') .aggregate(aggregation, { allowDiskUse: true }) .toArray(); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'getOcpiEndpoints', uniqueTimerID, ocpiEndpointsMDB); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'getOcpiEndpoints', uniqueTimerID, ocpiEndpointsMDB); // Ok return { count: (ocpiEndpointsCountMDB.length > 0 ? ocpiEndpointsCountMDB[0].count : 0), @@ -174,26 +175,26 @@ export default class OCPIEndpointStorage { }; } - static async deleteOcpiEndpoint(tenantID: string, id: string): Promise { + static async deleteOcpiEndpoint(tenant: Tenant, id: string): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'deleteOcpiEndpoint'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'deleteOcpiEndpoint'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Delete OcpiEndpoint - await global.database.getCollection(tenantID, 'ocpiendpoints') + await global.database.getCollection(tenant.id, 'ocpiendpoints') .findOneAndDelete({ '_id': DatabaseUtils.convertToObjectID(id) }); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'deleteOcpiEndpoint', uniqueTimerID, { id }); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'deleteOcpiEndpoint', uniqueTimerID, { id }); } - static async deleteOcpiEndpoints(tenantID: string): Promise { + static async deleteOcpiEndpoints(tenant: Tenant): Promise { // Debug - const uniqueTimerID = Logging.traceStart(tenantID, MODULE_NAME, 'deleteOcpiEndpoints'); + const uniqueTimerID = Logging.traceStart(tenant.id, MODULE_NAME, 'deleteOcpiEndpoints'); // Check Tenant - await DatabaseUtils.checkTenant(tenantID); + DatabaseUtils.checkTenantObject(tenant); // Delete OcpiEndpoint - await global.database.getCollection(tenantID, 'ocpiendpoints').deleteMany({}); + await global.database.getCollection(tenant.id, 'ocpiendpoints').deleteMany({}); // Debug - await Logging.traceEnd(tenantID, MODULE_NAME, 'deleteOcpiEndpoints', uniqueTimerID); + await Logging.traceEnd(tenant.id, MODULE_NAME, 'deleteOcpiEndpoints', uniqueTimerID); } } diff --git a/src/storage/mongodb/SiteAreaStorage.ts b/src/storage/mongodb/SiteAreaStorage.ts index 320aa4667b..0cb2937ef3 100644 --- a/src/storage/mongodb/SiteAreaStorage.ts +++ b/src/storage/mongodb/SiteAreaStorage.ts @@ -76,7 +76,7 @@ export default class SiteAreaStorage { } public static async getSiteArea(tenantID: string, id: string = Constants.UNKNOWN_OBJECT_ID, - params: { withSite?: boolean; withChargingStations?: boolean,withAvailableChargingStations?: boolean; withImage?: boolean } = {}, + params: { withSite?: boolean; withChargingStations?: boolean, withAvailableChargingStations?: boolean; withImage?: boolean } = {}, projectFields?: string[]): Promise { const siteAreasMDB = await SiteAreaStorage.getSiteAreas(tenantID, { siteAreaIDs: [id], @@ -136,7 +136,7 @@ export default class SiteAreaStorage { public static async getSiteAreas(tenantID: string, params: { - siteAreaIDs?: string[]; search?: string; siteIDs?: string[]; withSite?: boolean; issuer?: boolean; name?: string; + siteAreaIDs?: string[]; search?: string; siteIDs?: string[];companyIDs?: string[]; withSite?: boolean; issuer?: boolean; name?: string; withChargingStations?: boolean; withOnlyChargingStations?: boolean; withAvailableChargingStations?: boolean; locCoordinates?: number[]; locMaxDistanceMeters?: number; smartCharging?: boolean; withImage?: boolean; } = {}, @@ -187,6 +187,17 @@ export default class SiteAreaStorage { $in: params.siteIDs.map((siteID) => DatabaseUtils.convertToObjectID(siteID)) }; } + // Company + if (!Utils.isEmptyArray(params.companyIDs)) { + DatabaseUtils.pushSiteLookupInAggregation({ + tenantID, aggregation, localField: 'siteID', foreignField: '_id', + asField: 'site', oneToOneCardinality: true + }); + filters['site.companyID'] = { + $in: params.companyIDs.map((companyID) => companyID) + }; + params.withSite = false; + } if (Utils.objectHasProperty(params, 'issuer') && Utils.isBoolean(params.issuer)) { filters.issuer = params.issuer; } @@ -337,8 +348,9 @@ export default class SiteAreaStorage { { '_id': { $in: chargingStationIDs } }, { $set: { + companyID: DatabaseUtils.convertToObjectID(siteArea.site?.companyID), + siteID: DatabaseUtils.convertToObjectID(siteArea.siteID), siteAreaID: DatabaseUtils.convertToObjectID(siteArea.id), - siteID: DatabaseUtils.convertToObjectID(siteArea.siteID) } }); } @@ -361,8 +373,9 @@ export default class SiteAreaStorage { { '_id': { $in: chargingStationIDs } }, { $set: { + companyID: null, + siteID: null, siteAreaID: null, - siteID: null } }); } diff --git a/src/storage/mongodb/SiteStorage.ts b/src/storage/mongodb/SiteStorage.ts index 227fec47a8..6401c29ac0 100644 --- a/src/storage/mongodb/SiteStorage.ts +++ b/src/storage/mongodb/SiteStorage.ts @@ -470,7 +470,7 @@ export default class SiteStorage { if (params.withOnlyChargingStations || params.withAvailableChargingStations) { // Get the chargers const chargingStations = await ChargingStationStorage.getChargingStations(tenant.id, - { siteIDs: [siteMDB.id], includeDeleted: false }, Constants.DB_PARAMS_MAX_LIMIT); + { siteIDs: [siteMDB.id], includeDeleted: false, withSiteArea: true }, Constants.DB_PARAMS_MAX_LIMIT); // Skip site with no charging stations if asked if (params.withOnlyChargingStations && chargingStations.count === 0) { continue; diff --git a/src/storage/mongodb/TransactionStorage.ts b/src/storage/mongodb/TransactionStorage.ts index fb7b810e2d..e42859cd96 100644 --- a/src/storage/mongodb/TransactionStorage.ts +++ b/src/storage/mongodb/TransactionStorage.ts @@ -53,6 +53,7 @@ export default class TransactionStorage { const transactionMDB: any = { _id: Utils.convertToInt(transactionToSave.id), issuer: Utils.convertToBoolean(transactionToSave.issuer), + companyID: DatabaseUtils.convertToObjectID(transactionToSave.companyID), siteID: DatabaseUtils.convertToObjectID(transactionToSave.siteID), siteAreaID: DatabaseUtils.convertToObjectID(transactionToSave.siteAreaID), connectorId: Utils.convertToInt(transactionToSave.connectorId), @@ -273,7 +274,7 @@ export default class TransactionStorage { chargeBoxIDs?: string[]; siteAreaIDs?: string[]; siteIDs?: string[]; connectorIDs?: number[]; startDateTime?: Date; 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; + ocpiSessionChecked?: boolean; ocpiCdrChecked?: boolean; oicpSessionID?: string; withSite?: boolean; withSiteArea?: boolean; withCompany?: boolean; statistics?: 'refund' | 'history' | 'ongoing'; refundStatus?: string[]; withTag?: boolean; hasUserID?: boolean; }, dbParams: DbParams, projectFields?: string[]): @@ -615,18 +616,39 @@ export default class TransactionStorage { } }); } - // Transaction tag + // Tag if (params.withTag) { DatabaseUtils.pushTagLookupInAggregation({ tenantID, aggregation: aggregation, asField: 'tag', localField: 'tagID', foreignField: '_id', oneToOneCardinality: true }); } - // Charge Box + // Charging Station DatabaseUtils.pushChargingStationLookupInAggregation({ tenantID, aggregation: aggregation, localField: 'chargeBoxID', foreignField: '_id', asField: 'chargeBox', oneToOneCardinality: true, oneToOneCardinalityNotNull: false }); + // Company + if (params.withCompany) { + DatabaseUtils.pushCompanyLookupInAggregation({ + tenantID, aggregation: aggregation, localField: 'companyID', foreignField: '_id', + asField: 'company', oneToOneCardinality: true + }); + } + // Site + if (params.withSite) { + DatabaseUtils.pushSiteLookupInAggregation({ + tenantID, aggregation: aggregation, localField: 'siteID', foreignField: '_id', + asField: 'site', oneToOneCardinality: true + }); + } + // Site Area + if (params.withSiteArea) { + DatabaseUtils.pushSiteAreaLookupInAggregation({ + tenantID, aggregation: aggregation, localField: 'siteAreaID', foreignField: '_id', + asField: 'siteArea', oneToOneCardinality: true + }); + } DatabaseUtils.pushConvertObjectIDToString(aggregation, 'chargeBox.siteAreaID'); // Add Connector and Status if (projectFields && projectFields.includes('status')) { @@ -1317,15 +1339,7 @@ export default class TransactionStorage { return [ { $match: { - $and: [ - { - $or: [ - { 'userID': null }, - { 'user': null }, - ] - }, - { 'siteArea.accessControl': { '$eq': true } } - ] + 'userID': null, } }, { $addFields: { 'errorCode': TransactionInErrorType.MISSING_USER } } @@ -1341,8 +1355,6 @@ export default class TransactionStorage { { 'billingData': { $exists: false } }, { 'billingData.stop': { $exists: false } }, { 'billingData.stop.status': { $eq: BillingStatus.FAILED } }, - // { 'billingData.stop.invoiceID': { $exists: false } }, - // { 'billingData.stop.invoiceID': { $eq: null } } ] } ] diff --git a/src/types/ChargingStation.ts b/src/types/ChargingStation.ts index 2a51cb3f34..3a6ac15163 100644 --- a/src/types/ChargingStation.ts +++ b/src/types/ChargingStation.ts @@ -7,6 +7,7 @@ import { KeyValue } from './GlobalType'; import { OCPIEvse } from './ocpi/OCPIEvse'; import { OICPEvseDataRecord } from './oicp/OICPEvse'; import { OICPIdentification } from './oicp/OICPIdentification'; +import Site from './Site'; import SiteArea from './SiteArea'; import User from './User'; @@ -21,6 +22,7 @@ export default interface ChargingStation extends CreatedUpdatedProps { public: boolean; siteAreaID?: string; siteID?: string; + companyID?: string; chargePointSerialNumber: string; chargePointModel: string; chargeBoxSerialNumber: string; @@ -54,6 +56,7 @@ export default interface ChargingStation extends CreatedUpdatedProps { remoteAuthorizations: RemoteAuthorization[]; currentIPAddress?: string|string[]; siteArea?: SiteArea; + site?: Site; capabilities?: ChargingStationCapabilities; ocppStandardParameters?: KeyValue[]; ocppVendorParameters?: KeyValue[]; diff --git a/src/types/Transaction.ts b/src/types/Transaction.ts index 0d4cfafbe3..512844f580 100644 --- a/src/types/Transaction.ts +++ b/src/types/Transaction.ts @@ -4,12 +4,15 @@ import Consumption, { AbstractCurrentConsumption } from './Consumption'; import { BillingTransactionData } from './Billing'; import ChargingStation from '../types/ChargingStation'; +import Company from './Company'; import { OCPICdr } from './ocpi/OCPICdr'; import { OCPISession } from './ocpi/OCPISession'; import { OICPChargeDetailRecord } from './oicp/OICPChargeDetailRecord'; import { OICPSession } from './oicp/OICPSession'; import { PricingModel } from './Pricing'; import { RefundTransactionData } from './Refund'; +import Site from './Site'; +import SiteArea from './SiteArea'; import Tag from './Tag'; import User from './User'; @@ -52,8 +55,12 @@ export default interface Transaction extends AbstractCurrentConsumption { carCatalogID?: number; carCatalog?: CarCatalog; phasesUsed?: CSPhasesUsed; + companyID?: string; + company?: Company; siteID?: string; + site?: Site; siteAreaID?: string; + siteArea?: SiteArea; issuer: boolean; connectorId: number; tagID: string; diff --git a/src/types/ocpp/OCPPServer.ts b/src/types/ocpp/OCPPServer.ts index f2e6c64b51..0688da4dd0 100644 --- a/src/types/ocpp/OCPPServer.ts +++ b/src/types/ocpp/OCPPServer.ts @@ -357,6 +357,7 @@ export interface OCPPStartTransactionRequestExtended extends OCPPStartTransactio userID: string; siteAreaID: string; siteID: string; + companyID: string; } export interface OCPPStartTransactionResponse { diff --git a/src/types/requests/HttpChargingStationRequest.ts b/src/types/requests/HttpChargingStationRequest.ts index e4b0acc50b..43e188591b 100644 --- a/src/types/requests/HttpChargingStationRequest.ts +++ b/src/types/requests/HttpChargingStationRequest.ts @@ -38,7 +38,9 @@ export interface HttpChargingStationsRequest extends HttpDatabaseRequest { ConnectorType?: string; ChargingStationID?: string; SiteID?: string; + CompanyID?: string; WithSite?: boolean; + WithSiteArea?: boolean; SiteAreaID?: string; IncludeDeleted?: boolean; ErrorType?: string; diff --git a/src/types/requests/HttpSiteAreaRequest.ts b/src/types/requests/HttpSiteAreaRequest.ts index baebab9f3a..1c41a4f6c6 100644 --- a/src/types/requests/HttpSiteAreaRequest.ts +++ b/src/types/requests/HttpSiteAreaRequest.ts @@ -11,6 +11,7 @@ export interface HttpSiteAreasRequest extends HttpDatabaseRequest { Issuer: boolean; Search: string; SiteID?: string; + CompanyID?: string; WithSite?: boolean; WithChargeBoxes?: boolean; WithAvailableChargers: boolean; diff --git a/src/types/requests/HttpTransactionRequest.ts b/src/types/requests/HttpTransactionRequest.ts index 72d19f577e..cf65a958b5 100644 --- a/src/types/requests/HttpTransactionRequest.ts +++ b/src/types/requests/HttpTransactionRequest.ts @@ -25,6 +25,9 @@ export interface HttpPushTransactionCdrRequest { export interface HttpTransactionsRequest extends HttpDatabaseRequest { ChargingStationID: string; Issuer: boolean; + WithCompany: boolean; + WithSite: boolean; + WithSiteArea: boolean; ConnectorID: string; SiteAreaID?: string; SiteID?: string; diff --git a/test/api/OCPPCommonTests.ts b/test/api/OCPPCommonTests.ts index 039b6b5944..e5ef32ebf0 100644 --- a/test/api/OCPPCommonTests.ts +++ b/test/api/OCPPCommonTests.ts @@ -1146,8 +1146,8 @@ export default class OCPPCommonTests { 'tagID': this.transactionStartUser.tags[0].id, 'meterStart': meterStart, 'userID': this.transactionStartUser.id, - 'siteAreaID': this.chargingStationContext.getChargingStation().siteAreaID ? this.chargingStationContext.getChargingStation().siteAreaID : null, - 'siteID': this.chargingStationContext.getChargingStation().siteArea ? this.chargingStationContext.getChargingStation().siteArea.siteID : null, + 'siteAreaID': this.chargingStationContext.getChargingStation().siteAreaID, + 'siteID': this.chargingStationContext.getChargingStation().siteID, 'user': { 'id': this.transactionStartUser.id, 'name': this.transactionStartUser.name, diff --git a/test/api/SmartChargingTest.ts b/test/api/SmartChargingTest.ts index 5e0c371915..4a6b3c8155 100644 --- a/test/api/SmartChargingTest.ts +++ b/test/api/SmartChargingTest.ts @@ -5,7 +5,7 @@ import { StaticLimitAmps, Voltage } from '../../src/types/ChargingStation'; import Transaction, { CSPhasesUsed } from '../../src/types/Transaction'; import chai, { assert, expect } from 'chai'; -import AssetStorage from '../../src//storage/mongodb/AssetStorage'; +import AssetStorage from '../../src/storage/mongodb/AssetStorage'; import CentralServerService from './client/CentralServerService'; import { ChargingProfile } from '../../src/types/ChargingProfile'; import ChargingStationContext from './context/ChargingStationContext'; @@ -14,7 +14,9 @@ import Constants from '../../src/utils/Constants'; import ContextDefinition from './context/ContextDefinition'; import ContextProvider from './context/ContextProvider'; import Cypher from '../../src/utils/Cypher'; +import LoggingStorage from '../../src/storage/mongodb/LoggingStorage'; import MongoDBStorage from '../../src/storage/mongodb/MongoDBStorage'; +import { ServerAction } from '../../src/types/Server'; import SiteAreaContext from './context/SiteAreaContext'; import SiteContext from './context/SiteContext'; import SmartChargingFactory from '../../src/integration/smart-charging/SmartChargingFactory'; @@ -421,6 +423,18 @@ describe('Smart Charging Service', function() { expect(chargingProfiles[2].profile.chargingSchedule.chargingSchedulePeriod).containSubset(limit32); }); + it('Test if single phased charging station is excluded from root fuse correctly', async () => { + await smartChargingIntegration.buildChargingProfiles(testData.siteAreaContext.getSiteArea(), [testData.chargingStationContext1.getChargingStation().id]); + // Get the log of the site area limitation adjustment and check the root fuse + const log = await LoggingStorage.getLogs(testData.tenantContext.getTenant().id, { actions: [ServerAction.SMART_CHARGING], search: 'currently being used' }, Constants.DB_PARAMS_SINGLE_RECORD, null); + const siteArea = testData.siteAreaContext.getSiteArea(); + const siteAreaLimitPerPhase = Utils.createDecimal(siteArea.maximumPower).div(siteArea.voltage).div(3).toNumber(); + const connectorLimit = Utils.createDecimal(Utils.getChargingStationAmperage(testData.chargingStationContext1.getChargingStation(), null, 1)).toNumber(); + expect(log.result[0].detailedMessages).include('"fusePhase1": ' + (siteAreaLimitPerPhase - connectorLimit)); + expect(log.result[0].detailedMessages).include('"fusePhase2": ' + siteAreaLimitPerPhase); + expect(log.result[0].detailedMessages).include('"fusePhase3": ' + siteAreaLimitPerPhase); + }); + it('Test for 3 single phased cars charging with lower site area limit and one car on a single phased station (meter value received)', async () => { // Send meter values for both connectors of the 3 phased stations await TestData.sendMeterValue(Voltage.VOLTAGE_230, 32, transaction, testData.chargingStationContext, { csPhase1: false, csPhase2: true, csPhase3: false }); @@ -652,6 +666,16 @@ describe('Smart Charging Service', function() { expect(chargingProfiles[0].profile.chargingSchedule.chargingSchedulePeriod).containSubset(limit32); }); + it('Test if one charging connector is excluded from root fuse when charging station is excluded', async () => { + await smartChargingIntegration.buildChargingProfiles(testData.siteAreaContext.getSiteArea(), [testData.chargingStationContext.getChargingStation().id]); + const log = await LoggingStorage.getLogs(testData.tenantContext.getTenant().id, { actions: [ServerAction.SMART_CHARGING], search: 'currently being used' }, Constants.DB_PARAMS_SINGLE_RECORD, null); + const siteArea = testData.siteAreaContext.getSiteArea(); + expect(log.result[0].detailedMessages).include('"fusePhase1": ' + + Utils.createDecimal(siteArea.maximumPower).div(siteArea.voltage).minus( + Utils.getChargingStationAmperage(testData.chargingStationContext.getChargingStation(), null, 1)).toNumber() + ); + }); + it('Test for two cars charging', async () => { const transactionStartResponse = await testData.chargingStationContext.startTransaction(2, testData.userContext.tags[0].id, 180, new Date); const transactionResponse = await testData.centralUserService.transactionApi.readById(transactionStartResponse.transactionId); @@ -664,6 +688,16 @@ describe('Smart Charging Service', function() { expect(chargingProfiles[1].profile.chargingSchedule.chargingSchedulePeriod).containSubset(limit32); }); + it('Test if both charging connectors are excluded from root fuse when charging station is excluded', async () => { + await smartChargingIntegration.buildChargingProfiles(testData.siteAreaContext.getSiteArea(), [testData.chargingStationContext.getChargingStation().id]); + const log = await LoggingStorage.getLogs(testData.tenantContext.getTenant().id, { actions: [ServerAction.SMART_CHARGING], search: 'currently being used' }, Constants.DB_PARAMS_SINGLE_RECORD, null); + const siteArea = testData.siteAreaContext.getSiteArea(); + expect(log.result[0].detailedMessages).include('"fusePhase1": ' + + Utils.createDecimal(siteArea.maximumPower).div(siteArea.voltage).minus( + Utils.getChargingStationAmperage(testData.chargingStationContext.getChargingStation())).toNumber() + ); + }); + it('Test for two cars charging with lower site area limit', async () => { testData.siteAreaContext.getSiteArea().maximumPower = 10000; const chargingProfiles = await smartChargingIntegration.buildChargingProfiles(testData.siteAreaContext.getSiteArea()); @@ -854,6 +888,17 @@ describe('Smart Charging Service', function() { ]); }); + it('Test if whole charge point is excluded from root fuse when charging station is excluded (one connector charging)', async () => { + await smartChargingIntegration.buildChargingProfiles(testData.siteAreaContext.getSiteArea(), [testData.chargingStationContext.getChargingStation().id]); + const log = await LoggingStorage.getLogs(testData.tenantContext.getTenant().id, { actions: [ServerAction.SMART_CHARGING], search: 'currently being used' }, Constants.DB_PARAMS_SINGLE_RECORD, null); + const siteArea = testData.siteAreaContext.getSiteArea(); + const siteAreaLimitPerPhase = Utils.createDecimal(siteArea.maximumPower).div(siteArea.voltage).div(3).toNumber(); + const connectorLimitPerPhase = Utils.createDecimal(Utils.getChargingStationAmperage(testData.chargingStationContext.getChargingStation())).div(3).toNumber(); + expect(log.result[0].detailedMessages).include('"fusePhase1": ' + (siteAreaLimitPerPhase - connectorLimitPerPhase)); + expect(log.result[0].detailedMessages).include('"fusePhase2": ' + (siteAreaLimitPerPhase - connectorLimitPerPhase)); + expect(log.result[0].detailedMessages).include('"fusePhase3": ' + (siteAreaLimitPerPhase - connectorLimitPerPhase)); + }); + it('Test for two cars charging', async () => { const transactionStartResponse = await testData.chargingStationContext.startTransaction(2, testData.userContext.tags[0].id, 180, new Date); const transactionResponse = await testData.centralUserService.transactionApi.readById(transactionStartResponse.transactionId); @@ -892,6 +937,17 @@ describe('Smart Charging Service', function() { ]); }); + it('Test if only charge point is excluded from root fuse when charging station is excluded (two connectors charging)', async () => { + await smartChargingIntegration.buildChargingProfiles(testData.siteAreaContext.getSiteArea(), [testData.chargingStationContext.getChargingStation().id]); + const log = await LoggingStorage.getLogs(testData.tenantContext.getTenant().id, { actions: [ServerAction.SMART_CHARGING], search: 'currently being used' }, Constants.DB_PARAMS_SINGLE_RECORD, null); + const siteArea = testData.siteAreaContext.getSiteArea(); + const siteAreaLimitPerPhase = Utils.createDecimal(siteArea.maximumPower).div(siteArea.voltage).div(3).toNumber(); + const connectorLimitPerPhase = Utils.createDecimal(Utils.getChargingStationAmperage(testData.chargingStationContext.getChargingStation())).div(3).toNumber(); + expect(log.result[0].detailedMessages).include('"fusePhase1": ' + (siteAreaLimitPerPhase - connectorLimitPerPhase)); + expect(log.result[0].detailedMessages).include('"fusePhase2": ' + (siteAreaLimitPerPhase - connectorLimitPerPhase)); + expect(log.result[0].detailedMessages).include('"fusePhase3": ' + (siteAreaLimitPerPhase - connectorLimitPerPhase)); + }); + it('Test for two cars charging with lower site area limit', async () => { testData.siteAreaContext.getSiteArea().maximumPower = 100000; const chargingProfiles = await smartChargingIntegration.buildChargingProfiles(testData.siteAreaContext.getSiteArea()); @@ -1074,7 +1130,8 @@ describe('Smart Charging Service', function() { }); }); }); - describe('Test for ChargingStation refusing the charging profile', () => { + + describe('Test limit adjustments, when charging stations are excluded', () => { before(async () => { testData.siteContext = testData.tenantContext.getSiteContext(ContextDefinition.SITE_CONTEXTS.SITE_BASIC); testData.siteAreaContext = testData.siteContext.getSiteAreaContext(ContextDefinition.SITE_AREA_CONTEXTS.WITH_SMART_CHARGING_THREE_PHASED); @@ -1084,17 +1141,19 @@ describe('Smart Charging Service', function() { after(async () => { chargingStationConnector1Available.timestamp = new Date().toISOString(); + chargingStationConnector2Available.timestamp = new Date().toISOString(); await testData.chargingStationContext.setConnectorStatus(chargingStationConnector1Available); + await testData.chargingStationContext.setConnectorStatus(chargingStationConnector2Available); await testData.chargingStationContext1.setConnectorStatus(chargingStationConnector1Available); - + await testData.chargingStationContext1.setConnectorStatus(chargingStationConnector2Available); // Reset modifications on siteArea testData.siteAreaContext.getSiteArea().smartCharging = false; await testData.userService.siteAreaApi.update(testData.siteAreaContext.getSiteArea()); }); - - it('Check if charging station will be excluded from smart charging, when pushing fails', async () => { - testData.siteAreaContext.getSiteArea().maximumPower = 200000; + it('Check if one charging connector of three phased charging station will be excluded from smart charging on three phased site area', async () => { + testData.siteAreaContext.getSiteArea().maximumPower = 22080 + + Utils.createDecimal(limitMinThreePhased[0].limit).mul(testData.siteAreaContext.getSiteArea().voltage).toNumber() ; testData.siteAreaContext.getSiteArea().smartCharging = true; await testData.userService.siteAreaApi.update(testData.siteAreaContext.getSiteArea()); await testData.chargingStationContext.startTransaction(1, testData.userContext.tags[0].id, 180, new Date); @@ -1104,7 +1163,36 @@ describe('Smart Charging Service', function() { await testData.chargingStationContext1.setConnectorStatus(chargingStationConnector1Charging); const chargingProfiles = await smartChargingIntegration.buildChargingProfiles(testData.siteAreaContext.getSiteArea(), [testData.chargingStationContext.getChargingStation().id]); - expect(chargingProfiles[0].profile.chargingSchedule.chargingSchedulePeriod).containSubset(limit32); + expect(chargingProfiles.length).to.be.eq(1); + expect(chargingProfiles[0].profile.chargingSchedule.chargingSchedulePeriod).containSubset(limitMinSinglePhased); + }); + + it('Check if one charging connector of single phased station will be excluded only on one phase on three phased site area', async () => { + const chargingProfiles = await smartChargingIntegration.buildChargingProfiles(testData.siteAreaContext.getSiteArea(), + [testData.chargingStationContext1.getChargingStation().id]); + expect(chargingProfiles.length).to.be.eq(1); + expect(chargingProfiles[0].profile.chargingSchedule.chargingSchedulePeriod).containSubset(limitMinThreePhased); + }); + + it('Check if two charging connectors of three phased charging station will be excluded only on one phase on three phased site area', async () => { + await testData.chargingStationContext.startTransaction(2, testData.userContext.tags[0].id, 180, new Date); + chargingStationConnector2Charging.timestamp = new Date().toISOString(); + await testData.chargingStationContext.setConnectorStatus(chargingStationConnector2Charging); + const chargingProfiles = await smartChargingIntegration.buildChargingProfiles(testData.siteAreaContext.getSiteArea(), + [testData.chargingStationContext.getChargingStation().id]); + expect(chargingProfiles.length).to.be.eq(1); + expect(chargingProfiles[0].profile.chargingSchedule.chargingSchedulePeriod).containSubset(limit0); + }); + + it('Check if two charging connectors of single phased charging station will be excluded only on one phase on three phased site area', async () => { + await testData.chargingStationContext1.startTransaction(2, testData.userContext.tags[0].id, 180, new Date); + chargingStationConnector2Charging.timestamp = new Date().toISOString(); + await testData.chargingStationContext1.setConnectorStatus(chargingStationConnector2Charging); + const chargingProfiles = await smartChargingIntegration.buildChargingProfiles(testData.siteAreaContext.getSiteArea(), + [testData.chargingStationContext1.getChargingStation().id]); + expect(chargingProfiles.length).to.be.eq(2); + expect(chargingProfiles[0].profile.chargingSchedule.chargingSchedulePeriod).containSubset(limit0); + expect(chargingProfiles[1].profile.chargingSchedule.chargingSchedulePeriod).containSubset(limit0); }); }); diff --git a/test/api/context/ContextBuilder.ts b/test/api/context/ContextBuilder.ts index bf6bb75598..5c4221940f 100644 --- a/test/api/context/ContextBuilder.ts +++ b/test/api/context/ContextBuilder.ts @@ -196,7 +196,7 @@ export default class ContextBuilder { localToken: ContextBuilder.generateLocalToken(OCPIRole.CPO, tenantContextDef.subdomain), token: 'TOIOP-OCPI-TOKEN-cpo-xxxx-xxxx-yyyy' } as OCPIEndpoint; - await OCPIEndpointStorage.saveOcpiEndpoint(buildTenant.id, cpoEndpoint); + await OCPIEndpointStorage.saveOcpiEndpoint(buildTenant, cpoEndpoint); const emspEndpoint = { name: 'EMSP Endpoint', role: OCPIRole.EMSP, @@ -209,7 +209,7 @@ export default class ContextBuilder { localToken: ContextBuilder.generateLocalToken(OCPIRole.EMSP, tenantContextDef.subdomain), token: 'TOIOP-OCPI-TOKEN-emsp-xxxx-xxxx-yyyy' } as OCPIEndpoint; - await OCPIEndpointStorage.saveOcpiEndpoint(buildTenant.id, emspEndpoint); + await OCPIEndpointStorage.saveOcpiEndpoint(buildTenant, emspEndpoint); } } } diff --git a/test/factories/ChargingStationFactory.ts b/test/factories/ChargingStationFactory.ts index 802487f67e..aeef21f3fb 100644 --- a/test/factories/ChargingStationFactory.ts +++ b/test/factories/ChargingStationFactory.ts @@ -7,7 +7,7 @@ const chargingStationThreePhased = Factory.define('chargingStation') .attr('chargePointModel', () => 'MONOBLOCK') .attr('chargePointSerialNumber', () => faker.random.alphaNumeric(25)) .attr('chargeBoxSerialNumber', () => 'EV.2S22P44' + faker.random.alphaNumeric(15).toUpperCase()) - .attr('firmwareVersion', () => '3.2.' + faker.datatype.number(9).toString()); + .attr('firmwareVersion', () => '3.2.0.' + faker.datatype.number(9).toString()); const chargingStationUnknownThreePhased = Factory.define('chargingStation') .attr('chargePointVendor', () => 'Unknown') @@ -21,7 +21,7 @@ const chargingStationSinglePhased = Factory.define('chargingStation') .attr('chargePointModel', () => 'MONOBLOCK') .attr('chargePointSerialNumber', () => faker.random.alphaNumeric(25)) .attr('chargeBoxSerialNumber', () => 'EV.2S7P44' + faker.random.alphaNumeric(15).toUpperCase()) - .attr('firmwareVersion', () => '3.2.' + faker.datatype.number(9).toString()); + .attr('firmwareVersion', () => '3.2.0.' + faker.datatype.number(9).toString()); const chargingStationDC = Factory.define('chargingStation') .attr('chargePointVendor', () => 'DELTA')