diff --git a/.vscode/settings-template.json b/.vscode/settings-template.json index a0e38e1f5e..fc477eb7d3 100644 --- a/.vscode/settings-template.json +++ b/.vscode/settings-template.json @@ -7,5 +7,5 @@ "typescript" ], "jest.jestCommandLine": "npm run test -- ", - "jest.autoRun": "false" + "jest.autoRun": { "watch": "false" } } diff --git a/src/app/pages/statistics/consumption/statistics-consumption.component.html b/src/app/pages/statistics/consumption/statistics-consumption.component.html index 4a5553b200..be1c83b916 100644 --- a/src/app/pages/statistics/consumption/statistics-consumption.component.html +++ b/src/app/pages/statistics/consumption/statistics-consumption.component.html @@ -1,6 +1,6 @@

diff --git a/src/app/pages/statistics/consumption/statistics-consumption.component.ts b/src/app/pages/statistics/consumption/statistics-consumption.component.ts index f15c497032..46e9082cc5 100644 --- a/src/app/pages/statistics/consumption/statistics-consumption.component.ts +++ b/src/app/pages/statistics/consumption/statistics-consumption.component.ts @@ -1,7 +1,10 @@ import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { ChartData } from 'chart.js'; +import { StatisticsAuthorizations } from 'types/Authorization'; import { ChartTypeValues } from 'types/Chart'; +import { StatisticDataResult } from 'types/DataResult'; +import { Utils } from 'utils/Utils'; import { CentralServerService } from '../../../services/central-server.service'; import { LocaleService } from '../../../services/locale.service'; @@ -27,6 +30,7 @@ export class StatisticsConsumptionComponent implements OnInit { public selectedYear!: number; public allYears = true; public chartsInitialized = false; + public authorizations: StatisticsAuthorizations; private filterParams!: FilterParams; private barChart!: SimpleChart; @@ -156,11 +160,14 @@ export class StatisticsConsumptionComponent implements OnInit { } public buildCharts(): void { + // Append withAuth filter to retrieve auth - this also changes the response into datasource + this.filterParams['WithAuth'] = 'true'; this.spinnerService.show(); if (this.selectedCategory === 'C') { this.centralServerService.getChargingStationConsumptionStatistics(this.selectedYear, this.filterParams) .subscribe((statisticsData) => { - this.barChartData = this.statisticsBuildService.buildStackedChartDataForMonths(statisticsData, 2); + this.initAuth(statisticsData); + this.barChartData = this.statisticsBuildService.buildStackedChartDataForMonths(statisticsData.result, 2); this.pieChartData = this.statisticsBuildService.calculateTotalChartDataFromStackedChartData(this.barChartData); this.totalConsumption = this.statisticsBuildService.calculateTotalValueFromChartData(this.barChartData); if (this.selectedChart === 'month') { @@ -173,7 +180,8 @@ export class StatisticsConsumptionComponent implements OnInit { } else { this.centralServerService.getUserConsumptionStatistics(this.selectedYear, this.filterParams) .subscribe((statisticsData) => { - this.barChartData = this.statisticsBuildService.buildStackedChartDataForMonths(statisticsData, 2); + this.initAuth(statisticsData); + this.barChartData = this.statisticsBuildService.buildStackedChartDataForMonths(statisticsData.result, 2); this.pieChartData = this.statisticsBuildService.calculateTotalChartDataFromStackedChartData(this.barChartData); this.totalConsumption = this.statisticsBuildService.calculateTotalValueFromChartData(this.barChartData); @@ -186,4 +194,14 @@ export class StatisticsConsumptionComponent implements OnInit { }); } } + + private initAuth(statisticsData: StatisticDataResult) { + this.authorizations = { + canListUsers: Utils.convertToBoolean(statisticsData.canListUsers), + canListChargingStations: Utils.convertToBoolean(statisticsData.canListChargingStations), + canListSites: Utils.convertToBoolean(statisticsData.canListSites), + canListSiteAreas: Utils.convertToBoolean(statisticsData.canListSiteAreas), + canExport: Utils.convertToBoolean(statisticsData.canExport), + }; + } } diff --git a/src/app/pages/statistics/inactivity/statistics-inactivity.component.html b/src/app/pages/statistics/inactivity/statistics-inactivity.component.html index b7fb69e90f..0bf9f4a2d3 100644 --- a/src/app/pages/statistics/inactivity/statistics-inactivity.component.html +++ b/src/app/pages/statistics/inactivity/statistics-inactivity.component.html @@ -1,6 +1,6 @@

diff --git a/src/app/pages/statistics/inactivity/statistics-inactivity.component.ts b/src/app/pages/statistics/inactivity/statistics-inactivity.component.ts index f548ddc252..bd6e62c345 100644 --- a/src/app/pages/statistics/inactivity/statistics-inactivity.component.ts +++ b/src/app/pages/statistics/inactivity/statistics-inactivity.component.ts @@ -1,13 +1,15 @@ import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { ChartData } from 'chart.js'; +import { StatisticsAuthorizations } from 'types/Authorization'; import { ChartTypeValues } from 'types/Chart'; +import { StatisticDataResult } from 'types/DataResult'; +import { Utils } from 'utils/Utils'; import { CentralServerService } from '../../../services/central-server.service'; import { LocaleService } from '../../../services/locale.service'; import { SpinnerService } from '../../../services/spinner.service'; import { FilterParams } from '../../../types/GlobalType'; -import { TableFilterDef } from '../../../types/Table'; import { SimpleChart } from '../shared/chart-utilities'; import { StatisticsBuildService } from '../shared/statistics-build.service'; import { StatisticsExportService } from '../shared/statistics-export.service'; @@ -28,6 +30,7 @@ export class StatisticsInactivityComponent implements OnInit { public selectedYear!: number; public allYears = true; public chartsInitialized = false; + public authorizations: StatisticsAuthorizations; private filterParams!: FilterParams; private barChart!: SimpleChart; @@ -157,11 +160,14 @@ export class StatisticsInactivityComponent implements OnInit { } public buildCharts(): void { + // Append withAuth filter to retrieve auth - this also changes the response into datasource + this.filterParams['WithAuth'] = 'true'; this.spinnerService.show(); if (this.selectedCategory === 'C') { this.centralServerService.getChargingStationInactivityStatistics(this.selectedYear, this.filterParams) .subscribe((statisticsData) => { - this.barChartData = this.statisticsBuildService.buildStackedChartDataForMonths(statisticsData, 2); + this.initAuth(statisticsData); + this.barChartData = this.statisticsBuildService.buildStackedChartDataForMonths(statisticsData.result, 2); this.pieChartData = this.statisticsBuildService.calculateTotalChartDataFromStackedChartData(this.barChartData); this.totalInactivity = this.statisticsBuildService.calculateTotalValueFromChartData(this.barChartData); if (this.selectedChart === 'month') { @@ -174,7 +180,8 @@ export class StatisticsInactivityComponent implements OnInit { } else { this.centralServerService.getUserInactivityStatistics(this.selectedYear, this.filterParams) .subscribe((statisticsData) => { - this.barChartData = this.statisticsBuildService.buildStackedChartDataForMonths(statisticsData, 2); + this.initAuth(statisticsData); + this.barChartData = this.statisticsBuildService.buildStackedChartDataForMonths(statisticsData.result, 2); this.pieChartData = this.statisticsBuildService.calculateTotalChartDataFromStackedChartData(this.barChartData); this.totalInactivity = this.statisticsBuildService.calculateTotalValueFromChartData(this.barChartData); if (this.selectedChart === 'month') { @@ -186,4 +193,14 @@ export class StatisticsInactivityComponent implements OnInit { }); } } + + private initAuth(statisticsData: StatisticDataResult) { + this.authorizations = { + canListUsers: Utils.convertToBoolean(statisticsData.canListUsers), + canListChargingStations: Utils.convertToBoolean(statisticsData.canListChargingStations), + canListSites: Utils.convertToBoolean(statisticsData.canListSites), + canListSiteAreas: Utils.convertToBoolean(statisticsData.canListSiteAreas), + canExport: Utils.convertToBoolean(statisticsData.canExport), + }; + } } diff --git a/src/app/pages/statistics/pricing/statistics-pricing.component.html b/src/app/pages/statistics/pricing/statistics-pricing.component.html index 3457d39ab3..7ac704612d 100644 --- a/src/app/pages/statistics/pricing/statistics-pricing.component.html +++ b/src/app/pages/statistics/pricing/statistics-pricing.component.html @@ -1,6 +1,6 @@

diff --git a/src/app/pages/statistics/pricing/statistics-pricing.component.ts b/src/app/pages/statistics/pricing/statistics-pricing.component.ts index e9ad41c490..0f00cd55c3 100644 --- a/src/app/pages/statistics/pricing/statistics-pricing.component.ts +++ b/src/app/pages/statistics/pricing/statistics-pricing.component.ts @@ -1,14 +1,16 @@ import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { ChartData } from 'chart.js'; +import { StatisticsAuthorizations } from 'types/Authorization'; import { ChartTypeValues } from 'types/Chart'; +import { StatisticDataResult } from 'types/DataResult'; +import { Utils } from 'utils/Utils'; import { CentralServerService } from '../../../services/central-server.service'; import { ComponentService } from '../../../services/component.service'; import { LocaleService } from '../../../services/locale.service'; import { SpinnerService } from '../../../services/spinner.service'; import { FilterParams } from '../../../types/GlobalType'; -import { TableFilterDef } from '../../../types/Table'; import { TenantComponents } from '../../../types/Tenant'; import { SimpleChart } from '../shared/chart-utilities'; import { StatisticsBuildService, StatisticsBuildValueWithUnit } from '../shared/statistics-build.service'; @@ -31,6 +33,7 @@ export class StatisticsPricingComponent implements OnInit { public selectedYear!: number; public allYears = true; public chartsInitialized = false; + public authorizations: StatisticsAuthorizations; private filterParams!: FilterParams; private barChart!: SimpleChart; @@ -178,6 +181,8 @@ export class StatisticsPricingComponent implements OnInit { } public buildCharts(): void { + // Append withAuth filter to retrieve auth - this also changes the response into datasource + this.filterParams['WithAuth'] = 'true'; this.spinnerService.show(); let newToolTipUnit: string; let newLabelYAxis: string; @@ -185,6 +190,7 @@ export class StatisticsPricingComponent implements OnInit { if (this.selectedCategory === 'C') { this.centralServerService.getChargingStationPricingStatistics(this.selectedYear, this.filterParams) .subscribe((statisticsData) => { + this.initAuth(statisticsData); this.totalPriceWithUnit = this.statisticsBuildService.calculateTotalsWithUnits(statisticsData, 2); if (this.totalPriceWithUnit.length > 1) { addUnitToLabel = true; @@ -193,7 +199,7 @@ export class StatisticsPricingComponent implements OnInit { newToolTipUnit = this.totalPriceWithUnit[0].unit; } newLabelYAxis = this.translateService.instant('statistics.graphic_title_pricing_y_axis', { currency: newToolTipUnit }); - this.barChartData = this.statisticsBuildService.buildStackedChartDataForMonths(statisticsData, 2, addUnitToLabel); + this.barChartData = this.statisticsBuildService.buildStackedChartDataForMonths(statisticsData.result, 2, addUnitToLabel); this.pieChartData = this.statisticsBuildService.calculateTotalChartDataFromStackedChartData(this.barChartData); this.barChart.updateChart(this.barChartData, this.getChartLabel(), newToolTipUnit, newLabelYAxis); this.pieChart.updateChart(this.pieChartData, this.getChartLabel(), newToolTipUnit); @@ -202,7 +208,8 @@ export class StatisticsPricingComponent implements OnInit { } else { this.centralServerService.getUserPricingStatistics(this.selectedYear, this.filterParams) .subscribe((statisticsData) => { - if (statisticsData.length > 1) { + this.initAuth(statisticsData); + if (statisticsData.count > 1) { this.totalPriceWithUnit = this.statisticsBuildService.calculateTotalsWithUnits(statisticsData, 2); } if (this.totalPriceWithUnit.length > 1) { @@ -212,7 +219,7 @@ export class StatisticsPricingComponent implements OnInit { newToolTipUnit = this.totalPriceWithUnit[0].unit; } newLabelYAxis = this.translateService.instant('statistics.graphic_title_pricing_y_axis', { currency: newToolTipUnit }); - this.barChartData = this.statisticsBuildService.buildStackedChartDataForMonths(statisticsData, 2, addUnitToLabel); + this.barChartData = this.statisticsBuildService.buildStackedChartDataForMonths(statisticsData.result, 2, addUnitToLabel); this.pieChartData = this.statisticsBuildService.calculateTotalChartDataFromStackedChartData(this.barChartData); this.barChart.updateChart(this.barChartData, this.getChartLabel(), newToolTipUnit, newLabelYAxis); this.pieChart.updateChart(this.pieChartData, this.getChartLabel(), newToolTipUnit); @@ -220,4 +227,14 @@ export class StatisticsPricingComponent implements OnInit { }); } } + + private initAuth(statisticsData: StatisticDataResult) { + this.authorizations = { + canListUsers: Utils.convertToBoolean(statisticsData.canListUsers), + canListChargingStations: Utils.convertToBoolean(statisticsData.canListChargingStations), + canListSites: Utils.convertToBoolean(statisticsData.canListSites), + canListSiteAreas: Utils.convertToBoolean(statisticsData.canListSiteAreas), + canExport: Utils.convertToBoolean(statisticsData.canExport), + }; + } } diff --git a/src/app/pages/statistics/shared/statistics-build.service.ts b/src/app/pages/statistics/shared/statistics-build.service.ts index de933ce601..094be32108 100644 --- a/src/app/pages/statistics/shared/statistics-build.service.ts +++ b/src/app/pages/statistics/shared/statistics-build.service.ts @@ -2,6 +2,7 @@ import { Injectable } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { ChartData, ChartDataset } from 'chart.js'; import * as moment from 'moment'; +import { StatisticDataResult } from 'types/DataResult'; import { LocaleService } from '../../../services/locale.service'; import { StatisticData } from '../../../types/Statistic'; @@ -312,7 +313,7 @@ export class StatisticsBuildService { return count; } - public calculateTotalsWithUnits(statisticsData: any, roundingDecimals: number = 0, ignoreEmptyUnit = true): StatisticsBuildValueWithUnit[] { + public calculateTotalsWithUnits(statisticsData: StatisticDataResult, roundingDecimals: number = 0, ignoreEmptyUnit = true): StatisticsBuildValueWithUnit[] { let roundingFactor = 1; let index = 0; let localString: any; @@ -322,7 +323,7 @@ export class StatisticsBuildService { let totalOfLastUnit = 0; let totalWithUnit: StatisticsBuildValueWithUnit; const totalsWithUnit: StatisticsBuildValueWithUnit[] = []; - const transactionValues = statisticsData; + const transactionValues = statisticsData.result; if (roundingDecimals !== 0) { if (roundingDecimals > 0) { for (let i = 0; i < roundingDecimals; i++) { diff --git a/src/app/pages/statistics/shared/statistics-filters.component.html b/src/app/pages/statistics/shared/statistics-filters.component.html index 429342c0a6..d5d239c21e 100644 --- a/src/app/pages/statistics/shared/statistics-filters.component.html +++ b/src/app/pages/statistics/shared/statistics-filters.component.html @@ -4,7 +4,7 @@
- @@ -61,7 +61,7 @@
-
+
diff --git a/src/app/pages/statistics/shared/statistics-filters.component.ts b/src/app/pages/statistics/shared/statistics-filters.component.ts index ccf48a03c4..d09078b34f 100644 --- a/src/app/pages/statistics/shared/statistics-filters.component.ts +++ b/src/app/pages/statistics/shared/statistics-filters.component.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, OnInit, Output, ViewChild } from '@angular/core'; import { MatDialog, MatDialogConfig } from '@angular/material/dialog'; import { MatDatetimepickerInputEvent } from '@mat-datetimepicker/core'; import { TranslateService } from '@ngx-translate/core'; @@ -10,11 +10,10 @@ import { IssuerFilter } from 'shared/table/filters/issuer-filter'; import { SiteAreaTableFilter } from 'shared/table/filters/site-area-table-filter'; import { SiteTableFilter } from 'shared/table/filters/site-table-filter'; import { UserTableFilter } from 'shared/table/filters/user-table-filter'; +import { StatisticsAuthorizations } from 'types/Authorization'; -import { AuthorizationService } from '../../../services/authorization.service'; import { CentralServerService } from '../../../services/central-server.service'; import { ComponentService } from '../../../services/component.service'; -import { LocaleService } from '../../../services/locale.service'; import { FilterParams } from '../../../types/GlobalType'; import { SettingLink } from '../../../types/Setting'; import { FilterType, TableFilterDef } from '../../../types/Table'; @@ -31,11 +30,12 @@ export interface StatisticsButtonGroup { selector: 'app-statistics-filters', templateUrl: 'statistics-filters.component.html', }) -export class StatisticsFiltersComponent implements OnInit { +export class StatisticsFiltersComponent implements OnInit, OnChanges { @ViewChild(DaterangepickerComponent) public dateRangePickerComponent: DaterangepickerComponent; @ViewChild(DaterangepickerDirective) public picker: DaterangepickerDirective; @Input() public allYears ?= false; + @Input() public authorizations: StatisticsAuthorizations; @Output() public category = new EventEmitter(); @Output() public year = new EventEmitter(); @@ -48,7 +48,6 @@ export class StatisticsFiltersComponent implements OnInit { @Output() public export = new EventEmitter(); public ongoingRefresh = false; - public isAdmin!: boolean; public selectedYear!: number; public transactionYears!: number[]; public sacLinks!: SettingLink[]; @@ -65,38 +64,47 @@ export class StatisticsFiltersComponent implements OnInit { private filterParams = {}; + private issuerFilter: TableFilterDef; + private dateRangeFilter: TableFilterDef; + private siteFilter: TableFilterDef; + private siteAreaFilter: TableFilterDef; + private chargingStationFilter: TableFilterDef; + private userFilter: TableFilterDef; + public constructor( - private authorizationService: AuthorizationService, private translateService: TranslateService, private componentService: ComponentService, private centralServerService: CentralServerService, - private localeService: LocaleService, private dialog: MatDialog) { - this.isAdmin = this.authorizationService.isAdmin() || this.authorizationService.isSuperAdmin(); - const issuerFilter = new IssuerFilter().getFilterDef(); - const dateRangeFilter = new DateRangeTableFilter({ + this.initFilters(); + } + + public initFilters() { + this.issuerFilter = new IssuerFilter().getFilterDef(); + this.dateRangeFilter = new DateRangeTableFilter({ translateService: this.translateService }).getFilterDef(); - this.tableFiltersDef.push(dateRangeFilter); - const siteFilter = new SiteTableFilter([issuerFilter]).getFilterDef(); - this.tableFiltersDef.push(siteFilter); - const siteAreaFilter = new SiteAreaTableFilter([issuerFilter, siteFilter]).getFilterDef(); - this.tableFiltersDef.push(siteAreaFilter); - const chargingStationFilter = new ChargingStationTableFilter([issuerFilter, siteFilter, siteAreaFilter]).getFilterDef(); - this.tableFiltersDef.push(chargingStationFilter); - const userFilter = new UserTableFilter([issuerFilter, siteFilter]).getFilterDef(); - this.tableFiltersDef.push(userFilter); - if (!this.componentService.isActive(TenantComponents.ORGANIZATION)) { - siteFilter.visible = false; - siteAreaFilter.visible = false; - } - if (this.isAdmin) { - userFilter.visible = true; - } + this.tableFiltersDef.push(this.dateRangeFilter); + this.siteFilter = new SiteTableFilter([this.issuerFilter]).getFilterDef(); + this.tableFiltersDef.push(this.siteFilter); + this.siteAreaFilter = new SiteAreaTableFilter([this.issuerFilter, this.siteFilter]).getFilterDef(); + this.tableFiltersDef.push(this.siteAreaFilter); + this.chargingStationFilter = new ChargingStationTableFilter([this.issuerFilter, this.siteFilter, this.siteAreaFilter]).getFilterDef(); + this.tableFiltersDef.push(this.chargingStationFilter); + this.userFilter = new UserTableFilter([this.issuerFilter, this.siteFilter]).getFilterDef(); + this.tableFiltersDef.push(this.userFilter); + this.updateFilterVisibilityWithAuth(); } - public openDateRange() { - this.picker.open(); + public ngOnChanges() { + this.updateFilterVisibilityWithAuth(); + } + + public updateFilterVisibilityWithAuth() { + this.siteFilter.visible = Utils.convertToBoolean(this.authorizations?.canListSites); + this.siteAreaFilter.visible = Utils.convertToBoolean(this.authorizations?.canListSiteAreas); + this.userFilter.visible = Utils.convertToBoolean(this.authorizations?.canListUsers); + this.chargingStationFilter.visible = Utils.convertToBoolean(this.authorizations?.canListChargingStations); } public ngOnInit(): void { @@ -120,16 +128,7 @@ export class StatisticsFiltersComponent implements OnInit { // Get SAC links if (this.componentService.isActive(TenantComponents.ANALYTICS)) { this.componentService.getSacSettings().subscribe((sacSettings) => { - if (this.isAdmin) { - this.sacLinks = sacSettings.links; - } else { - this.sacLinks = []; - for (const sacLink of sacSettings.links) { - if (sacLink.role === 'D') { - this.sacLinks.push(sacLink); - } - } - } + this.sacLinks = sacSettings.links; if (!Utils.isEmptyArray(this.sacLinks)) { this.sacLinksActive = true; } else { @@ -145,6 +144,10 @@ export class StatisticsFiltersComponent implements OnInit { this.update.emit(true); } + public openDateRange() { + this.picker.open(); + } + public dateRangeChange(filterDef: TableFilterDef, event): void { if (filterDef.type === 'date-range' && event.hasOwnProperty('startDate') && event.hasOwnProperty('endDate')) { filterDef.currentValue = event ? event : null; diff --git a/src/app/pages/statistics/statistics.component.ts b/src/app/pages/statistics/statistics.component.ts index ae28dacdcc..b5517fbab1 100644 --- a/src/app/pages/statistics/statistics.component.ts +++ b/src/app/pages/statistics/statistics.component.ts @@ -12,17 +12,14 @@ import { TenantComponents } from '../../types/Tenant'; templateUrl: 'statistics.component.html', }) export class StatisticsComponent extends AbstractTabComponent { - public isAdmin: boolean; public isPricingActive = false; public constructor( - private authorizationService: AuthorizationService, private componentService: ComponentService, activatedRoute: ActivatedRoute, windowService: WindowService, ) { super(activatedRoute, windowService, ['consumption', 'usage', 'inactivity', 'transactions', 'pricing']); - this.isAdmin = this.authorizationService.isAdmin(); this.isPricingActive = this.componentService.isActive(TenantComponents.PRICING); } } diff --git a/src/app/pages/statistics/transactions/statistics-transactions.component.html b/src/app/pages/statistics/transactions/statistics-transactions.component.html index da677b9e33..7d5a5b71f5 100644 --- a/src/app/pages/statistics/transactions/statistics-transactions.component.html +++ b/src/app/pages/statistics/transactions/statistics-transactions.component.html @@ -1,6 +1,6 @@

diff --git a/src/app/pages/statistics/transactions/statistics-transactions.component.ts b/src/app/pages/statistics/transactions/statistics-transactions.component.ts index ea16571171..e5535e237b 100644 --- a/src/app/pages/statistics/transactions/statistics-transactions.component.ts +++ b/src/app/pages/statistics/transactions/statistics-transactions.component.ts @@ -1,7 +1,10 @@ import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { ChartData } from 'chart.js'; +import { StatisticsAuthorizations } from 'types/Authorization'; import { ChartTypeValues } from 'types/Chart'; +import { StatisticDataResult } from 'types/DataResult'; +import { Utils } from 'utils/Utils'; import { CentralServerService } from '../../../services/central-server.service'; import { LocaleService } from '../../../services/locale.service'; @@ -28,6 +31,7 @@ export class StatisticsTransactionsComponent implements OnInit { public selectedYear!: number; public allYears = true; public chartsInitialized = false; + public authorizations: StatisticsAuthorizations; private filterParams!: FilterParams; private barChart!: SimpleChart; @@ -159,11 +163,14 @@ export class StatisticsTransactionsComponent implements OnInit { } public buildCharts(): void { + // Append withAuth filter to retrieve auth - this also changes the response into datasource + this.filterParams['WithAuth'] = 'true'; this.spinnerService.show(); if (this.selectedCategory === 'C') { this.centralServerService.getChargingStationTransactionsStatistics(this.selectedYear, this.filterParams) .subscribe((statisticsData) => { - this.barChartData = this.statisticsBuildService.buildStackedChartDataForMonths(statisticsData, 2); + this.initAuth(statisticsData); + this.barChartData = this.statisticsBuildService.buildStackedChartDataForMonths(statisticsData.result, 2); this.pieChartData = this.statisticsBuildService.calculateTotalChartDataFromStackedChartData(this.barChartData); this.totalTransactions = this.statisticsBuildService.calculateTotalValueFromChartData(this.barChartData); if (this.selectedChart === 'month') { @@ -176,7 +183,8 @@ export class StatisticsTransactionsComponent implements OnInit { } else { this.centralServerService.getUserTransactionsStatistics(this.selectedYear, this.filterParams) .subscribe((statisticsData) => { - this.barChartData = this.statisticsBuildService.buildStackedChartDataForMonths(statisticsData, 2); + this.initAuth(statisticsData); + this.barChartData = this.statisticsBuildService.buildStackedChartDataForMonths(statisticsData.result, 2); this.pieChartData = this.statisticsBuildService.calculateTotalChartDataFromStackedChartData(this.barChartData); this.totalTransactions = this.statisticsBuildService.calculateTotalValueFromChartData(this.barChartData); if (this.selectedChart === 'month') { @@ -188,4 +196,14 @@ export class StatisticsTransactionsComponent implements OnInit { }); } } + + private initAuth(statisticsData: StatisticDataResult) { + this.authorizations = { + canListUsers: Utils.convertToBoolean(statisticsData.canListUsers), + canListChargingStations: Utils.convertToBoolean(statisticsData.canListChargingStations), + canListSites: Utils.convertToBoolean(statisticsData.canListSites), + canListSiteAreas: Utils.convertToBoolean(statisticsData.canListSiteAreas), + canExport: Utils.convertToBoolean(statisticsData.canExport), + }; + } } diff --git a/src/app/pages/statistics/usage/statistics-usage.component.html b/src/app/pages/statistics/usage/statistics-usage.component.html index c04f8269e9..274aa444f5 100644 --- a/src/app/pages/statistics/usage/statistics-usage.component.html +++ b/src/app/pages/statistics/usage/statistics-usage.component.html @@ -1,6 +1,6 @@

diff --git a/src/app/pages/statistics/usage/statistics-usage.component.ts b/src/app/pages/statistics/usage/statistics-usage.component.ts index 7fc85b7417..ea715897a8 100644 --- a/src/app/pages/statistics/usage/statistics-usage.component.ts +++ b/src/app/pages/statistics/usage/statistics-usage.component.ts @@ -1,7 +1,10 @@ import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { ChartData } from 'chart.js'; +import { StatisticsAuthorizations } from 'types/Authorization'; import { ChartTypeValues } from 'types/Chart'; +import { StatisticDataResult } from 'types/DataResult'; +import { Utils } from 'utils/Utils'; import { CentralServerService } from '../../../services/central-server.service'; import { LocaleService } from '../../../services/locale.service'; @@ -27,6 +30,7 @@ export class StatisticsUsageComponent implements OnInit { public selectedYear!: number; public allYears = true; public chartsInitialized = false; + public authorizations: StatisticsAuthorizations; private filterParams = {}; private barChart!: SimpleChart; @@ -156,11 +160,14 @@ export class StatisticsUsageComponent implements OnInit { } public buildCharts(): void { + // Append withAuth filter to retrieve auth - this also changes the response into datasource + this.filterParams['WithAuth'] = 'true'; this.spinnerService.show(); if (this.selectedCategory === 'C') { this.centralServerService.getChargingStationUsageStatistics(this.selectedYear, this.filterParams) .subscribe((statisticsData) => { - this.barChartData = this.statisticsBuildService.buildStackedChartDataForMonths(statisticsData, 2); + this.initAuth(statisticsData); + this.barChartData = this.statisticsBuildService.buildStackedChartDataForMonths(statisticsData.result, 2); this.pieChartData = this.statisticsBuildService.calculateTotalChartDataFromStackedChartData(this.barChartData); this.totalUsage = this.statisticsBuildService.calculateTotalValueFromChartData(this.barChartData); if (this.selectedChart === 'month') { @@ -173,7 +180,8 @@ export class StatisticsUsageComponent implements OnInit { } else { this.centralServerService.getUserUsageStatistics(this.selectedYear, this.filterParams) .subscribe((statisticsData) => { - this.barChartData = this.statisticsBuildService.buildStackedChartDataForMonths(statisticsData, 2); + this.initAuth(statisticsData); + this.barChartData = this.statisticsBuildService.buildStackedChartDataForMonths(statisticsData.result, 2); this.pieChartData = this.statisticsBuildService.calculateTotalChartDataFromStackedChartData(this.barChartData); this.totalUsage = this.statisticsBuildService.calculateTotalValueFromChartData(this.barChartData); if (this.selectedChart === 'month') { @@ -185,4 +193,14 @@ export class StatisticsUsageComponent implements OnInit { }); } } + + private initAuth(statisticsData: StatisticDataResult) { + this.authorizations = { + canListUsers: Utils.convertToBoolean(statisticsData.canListUsers), + canListChargingStations: Utils.convertToBoolean(statisticsData.canListChargingStations), + canListSites: Utils.convertToBoolean(statisticsData.canListSites), + canListSiteAreas: Utils.convertToBoolean(statisticsData.canListSiteAreas), + canExport: Utils.convertToBoolean(statisticsData.canExport), + }; + } } diff --git a/src/app/release-notes/release-notes.component.ts b/src/app/release-notes/release-notes.component.ts index ba3c490ec7..4d8259d51c 100644 --- a/src/app/release-notes/release-notes.component.ts +++ b/src/app/release-notes/release-notes.component.ts @@ -25,15 +25,23 @@ export class ReleaseNotesComponent { this.releaseNotes = [ { version: '2.7.6', - date: new Date('2023-03-14'), + date: new Date('2023-03-28'), componentChanges: [ { name: 'Dashboard', changes: [ 'Smart Charging - Advanced Parameters for Departure Time', 'Smart Charging - Limit the number of periods to 16 (4 hours)', + 'Smart Charging - fix - default value not properly used', 'Billing - Stripe Integration - update dependency to the latest API version - 2022-11-15', - 'Security - removed vulnerability from strong-soap dependencies' + 'Pricing - fix - Delete pricing definitions', + 'Security - Removed vulnerability from strong-soap dependencies', + 'Statistics - Export to CSV files - filtering is now taken into account', + 'Authorizations - Migrated Statistics endpoints to the new authorization framework', + 'Authorizations - Site owners can change the power limit of a site', + 'OCPP - Allow a distinct CPMS Domain Name per Tenant', + 'Performance - Fix - Partial index creation', + 'Performance - some logs less verbose' ], }, ], diff --git a/src/app/services/central-server.service.ts b/src/app/services/central-server.service.ts index 1e02080146..d294470f03 100644 --- a/src/app/services/central-server.service.ts +++ b/src/app/services/central-server.service.ts @@ -15,7 +15,7 @@ import { ChargingStationTemplate } from '../types/ChargingStationTemplate'; import { Company } from '../types/Company'; import CentralSystemServerConfiguration from '../types/configuration/CentralSystemServerConfiguration'; import { IntegrationConnection, UserConnection } from '../types/Connection'; -import { ActionResponse, ActionsResponse, AssetDataResult, AssetInErrorDataResult, BillingAccountDataResult, BillingInvoiceDataResult, BillingOperationResult, BillingPaymentMethodDataResult, BillingTaxDataResult, BillingTransferDataResult, CarCatalogDataResult, CarDataResult, ChargingProfileDataResult, ChargingStationDataResult, ChargingStationInErrorDataResult, ChargingStationTemplateDataResult, CheckAssetConnectionResponse, CheckBillingConnectionResponse, CompanyDataResult, DataResult, LogDataResult, LoginResponse, OcpiEndpointDataResult, OCPIGenerateLocalTokenResponse, OCPIJobStatusesResponse, OCPIPingResponse, OICPJobStatusesResponse, OICPPingResponse, Ordering, Paging, PricingDefinitionDataResult, RegistrationTokenDataResult, SiteAreaDataResult, SiteDataResult, SiteUserDataResult, TagDataResult, TransactionDataResult, TransactionInErrorDataResult, UserDataResult, UserSiteDataResult } from '../types/DataResult'; +import { ActionResponse, ActionsResponse, AssetDataResult, AssetInErrorDataResult, BillingAccountDataResult, BillingInvoiceDataResult, BillingOperationResult, BillingPaymentMethodDataResult, BillingTaxDataResult, BillingTransferDataResult, CarCatalogDataResult, CarDataResult, ChargingProfileDataResult, ChargingStationDataResult, ChargingStationInErrorDataResult, ChargingStationTemplateDataResult, CheckAssetConnectionResponse, CheckBillingConnectionResponse, CompanyDataResult, DataResult, LogDataResult, LoginResponse, OCPIGenerateLocalTokenResponse, OCPIJobStatusesResponse, OCPIPingResponse, OICPJobStatusesResponse, OICPPingResponse, OcpiEndpointDataResult, Ordering, Paging, PricingDefinitionDataResult, RegistrationTokenDataResult, SiteAreaDataResult, SiteDataResult, SiteUserDataResult, StatisticDataResult, TagDataResult, TransactionDataResult, TransactionInErrorDataResult, UserDataResult, UserSiteDataResult } from '../types/DataResult'; import { EndUserLicenseAgreement } from '../types/Eula'; import { FilterParams, Image, KeyValue } from '../types/GlobalType'; import { Log } from '../types/Log'; @@ -29,7 +29,6 @@ import { RESTServerRoute, ServerAction } from '../types/Server'; import { BillingSettings, SettingDB } from '../types/Setting'; import { Site } from '../types/Site'; import { SiteArea, SiteAreaConsumption, SubSiteAreaAction } from '../types/SiteArea'; -import { StatisticData } from '../types/Statistic'; import { Tag } from '../types/Tag'; import { Tenant } from '../types/Tenant'; import { OcpiData, Transaction } from '../types/Transaction'; @@ -554,12 +553,12 @@ export class CentralServerService { } public getChargingStationConsumptionStatistics(year: number, - params: FilterParams = {}): Observable { + params: FilterParams = {}): Observable { params['Year'] = year + ''; // Verify init this.checkInit(); // Execute the REST service - return this.httpClient.get(this.buildRestEndpointUrl(RESTServerRoute.REST_CHARGING_STATION_CONSUMPTION_STATISTICS), + return this.httpClient.get(this.buildRestEndpointUrl(RESTServerRoute.REST_CHARGING_STATION_CONSUMPTION_STATISTICS), { headers: this.buildHttpHeaders(), params, @@ -570,12 +569,12 @@ export class CentralServerService { } public getUserConsumptionStatistics(year: number, - params: FilterParams = {}): Observable { + params: FilterParams = {}): Observable { params['Year'] = year + ''; // Verify init this.checkInit(); // Execute the REST service - return this.httpClient.get(this.buildRestEndpointUrl(RESTServerRoute.REST_USER_CONSUMPTION_STATISTICS), + return this.httpClient.get(this.buildRestEndpointUrl(RESTServerRoute.REST_USER_CONSUMPTION_STATISTICS), { headers: this.buildHttpHeaders(), params, @@ -586,12 +585,12 @@ export class CentralServerService { } public getChargingStationUsageStatistics(year: number, - params: FilterParams = {}): Observable { + params: FilterParams = {}): Observable { params['Year'] = year + ''; // Verify init this.checkInit(); // Execute the REST service - return this.httpClient.get(this.buildRestEndpointUrl(RESTServerRoute.REST_CHARGING_STATION_USAGE_STATISTICS), + return this.httpClient.get(this.buildRestEndpointUrl(RESTServerRoute.REST_CHARGING_STATION_USAGE_STATISTICS), { headers: this.buildHttpHeaders(), params, @@ -602,12 +601,12 @@ export class CentralServerService { } public getUserUsageStatistics(year: number, - params: FilterParams = {}): Observable { + params: FilterParams = {}): Observable { params['Year'] = year + ''; // Verify init this.checkInit(); // Execute the REST service - return this.httpClient.get(this.buildRestEndpointUrl(RESTServerRoute.REST_USER_USAGE_STATISTICS), + return this.httpClient.get(this.buildRestEndpointUrl(RESTServerRoute.REST_USER_USAGE_STATISTICS), { headers: this.buildHttpHeaders(), params, @@ -618,12 +617,12 @@ export class CentralServerService { } public getChargingStationInactivityStatistics(year: number, - params: FilterParams = {}): Observable { + params: FilterParams = {}): Observable { params['Year'] = year + ''; // Verify init this.checkInit(); // Execute the REST service - return this.httpClient.get(this.buildRestEndpointUrl(RESTServerRoute.REST_CHARGING_STATION_INACTIVITY_STATISTICS), + return this.httpClient.get(this.buildRestEndpointUrl(RESTServerRoute.REST_CHARGING_STATION_INACTIVITY_STATISTICS), { headers: this.buildHttpHeaders(), params, @@ -634,12 +633,12 @@ export class CentralServerService { } public getUserInactivityStatistics(year: number, - params: FilterParams = {}): Observable { + params: FilterParams = {}): Observable { params['Year'] = year + ''; // Verify init this.checkInit(); // Execute the REST service - return this.httpClient.get(this.buildRestEndpointUrl(RESTServerRoute.REST_USER_INACTIVITY_STATISTICS), + return this.httpClient.get(this.buildRestEndpointUrl(RESTServerRoute.REST_USER_INACTIVITY_STATISTICS), { headers: this.buildHttpHeaders(), params, @@ -669,12 +668,12 @@ export class CentralServerService { } public getChargingStationTransactionsStatistics(year: number, - params: FilterParams = {}): Observable { + params: FilterParams = {}): Observable { params['Year'] = year + ''; // Verify init this.checkInit(); // Execute the REST service - return this.httpClient.get(this.buildRestEndpointUrl(RESTServerRoute.REST_CHARGING_STATION_TRANSACTIONS_STATISTICS), + return this.httpClient.get(this.buildRestEndpointUrl(RESTServerRoute.REST_CHARGING_STATION_TRANSACTIONS_STATISTICS), { headers: this.buildHttpHeaders(), params, @@ -685,12 +684,12 @@ export class CentralServerService { } public getUserTransactionsStatistics(year: number, - params: FilterParams = {}): Observable { + params: FilterParams = {}): Observable { params['Year'] = year + ''; // Verify init this.checkInit(); // Execute the REST service - return this.httpClient.get(this.buildRestEndpointUrl(RESTServerRoute.REST_USER_TRANSACTIONS_STATISTICS), + return this.httpClient.get(this.buildRestEndpointUrl(RESTServerRoute.REST_USER_TRANSACTIONS_STATISTICS), { headers: this.buildHttpHeaders(), params, @@ -701,12 +700,12 @@ export class CentralServerService { } public getChargingStationPricingStatistics(year: number, - params: FilterParams = {}): Observable { + params: FilterParams = {}): Observable { params['Year'] = year + ''; // Verify init this.checkInit(); // Execute the REST service - return this.httpClient.get(this.buildRestEndpointUrl(RESTServerRoute.REST_CHARGING_STATION_PRICING_STATISTICS), + return this.httpClient.get(this.buildRestEndpointUrl(RESTServerRoute.REST_CHARGING_STATION_PRICING_STATISTICS), { headers: this.buildHttpHeaders(), params, @@ -717,12 +716,12 @@ export class CentralServerService { } public getUserPricingStatistics(year: number, - params: FilterParams = {}): Observable { + params: FilterParams = {}): Observable { params['Year'] = year + ''; // Verify init this.checkInit(); // Execute the REST service - return this.httpClient.get(this.buildRestEndpointUrl(RESTServerRoute.REST_USER_PRICING_STATISTICS), + return this.httpClient.get(this.buildRestEndpointUrl(RESTServerRoute.REST_USER_PRICING_STATISTICS), { headers: this.buildHttpHeaders(), params, diff --git a/src/app/types/Authorization.ts b/src/app/types/Authorization.ts index 8629c25584..f70baf5c2b 100644 --- a/src/app/types/Authorization.ts +++ b/src/app/types/Authorization.ts @@ -465,6 +465,17 @@ export interface OcpiEndpointAuthorizationActions extends AuthorizationActions { canTriggerJob?: boolean; } +export interface StatisticsAuthorizations extends AuthorizationAttributes, StatisticsAuthorizationActions { + canListUsers?: boolean; + canListChargingStations?: boolean; + canListSites?: boolean; + canListSiteAreas?: boolean; + canExport?: boolean; +} + +export interface StatisticsAuthorizationActions extends AuthorizationActions { +} + export enum DialogMode { EDIT = 'E', CREATE = 'C', diff --git a/src/app/types/DataResult.ts b/src/app/types/DataResult.ts index 5bc7f3bd63..f60f11c6fb 100644 --- a/src/app/types/DataResult.ts +++ b/src/app/types/DataResult.ts @@ -1,23 +1,23 @@ -import { AssetInError, ChargingStationInError, TransactionInError } from './InError'; -import { AssetsAuthorizations, BillingAccountsAuthorizations, BillingInvoicesAuthorizations, BillingPaymentMethodsAuthorizationActions, BillingTaxesAuthorizations, BillingTransfersAuthorizations, CarCatalogsAuthorizations, CarsAuthorizations, ChargingProfilesAuthorizations, ChargingStationTemplateAuthorizationActions, ChargingStationsAuthorizations, DataResultAuthorizations, LogsAuthorizationActions, OcpiEndpointsAuthorizationActions, SettingsAuthorizationActions, SiteUsersAuthorizations, SitesAuthorizationActions, TagsAuthorizations, TransactionsAuthorizations, UserSitesAuthorizations, UsersAuthorizations } from './Authorization'; +import { Asset } from './Asset'; +import { AssetsAuthorizations, BillingAccountsAuthorizations, BillingInvoicesAuthorizations, BillingPaymentMethodsAuthorizationActions, BillingTaxesAuthorizations, BillingTransfersAuthorizations, CarCatalogsAuthorizations, CarsAuthorizations, ChargingProfilesAuthorizations, ChargingStationTemplateAuthorizationActions, ChargingStationsAuthorizations, DataResultAuthorizations, LogsAuthorizationActions, OcpiEndpointsAuthorizationActions, SettingsAuthorizationActions, SiteUsersAuthorizations, SitesAuthorizationActions, StatisticsAuthorizations, TagsAuthorizations, TransactionsAuthorizations, UserSitesAuthorizations, UsersAuthorizations } from './Authorization'; import { BillingAccount, BillingInvoice, BillingPaymentMethod, BillingTax, BillingTransfer } from './Billing'; import { Car, CarCatalog } from './Car'; -import { Site, UserSite } from './Site'; -import { SiteUser, User, UserStatus } from './User'; - -import { Asset } from './Asset'; import { ChargingProfile } from './ChargingProfile'; import { ChargingStation } from './ChargingStation'; import { ChargingStationTemplate } from './ChargingStationTemplate'; import { Company } from './Company'; +import { AssetInError, ChargingStationInError, TransactionInError } from './InError'; import { Log } from './Log'; import { OCPIEndpoint } from './ocpi/OCPIEndpoint'; import PricingDefinition from './Pricing'; import { RegistrationToken } from './RegistrationToken'; import { Setting } from './Setting'; +import { Site, UserSite } from './Site'; import { SiteArea } from './SiteArea'; +import { StatisticData } from './Statistic'; import { Tag } from './Tag'; import { Transaction } from './Transaction'; +import { SiteUser, User, UserStatus } from './User'; export interface ActionResponse { status: string; @@ -198,6 +198,9 @@ export interface OcpiEndpointDataResult extends DataResult, OcpiEn canCreate?: boolean; } +export interface StatisticDataResult extends DataResult, StatisticsAuthorizations { +} + export interface Ordering { field: string; }