pricing - static restriction - check date validity
MODIFY
src/integration/pricing/PricingEngine.ts
src/integration/pricing/PricingEngine.ts
import FeatureToggles, { Feature } from '../../utils/FeatureToggles';
/* eslint-disable @typescript-eslint/member-ordering */
-import PricingDefinition, { DimensionType, PricedConsumptionData, PricedDimensionData, PricingDimension, PricingRestriction, ResolvedPricingModel } from '../../types/Pricing';
+import PricingDefinition, { DimensionType, PricedConsumptionData, PricedDimensionData, PricingDimension, PricingRestriction, PricingStaticRestriction, ResolvedPricingModel } from '../../types/Pricing';
import ChargingStation from '../../types/ChargingStation';
+import Constants from '../../utils/Constants';
import Consumption from '../../types/Consumption';
import PricingStorage from '../../storage/mongodb/PricingStorage';
import Tenant from '../../types/Tenant';
import Transaction from '../../types/Transaction';
import Utils from '../../utils/Utils';
+import moment from 'moment';
// --------------------------------------------------------------------------------------------------
// TODO - POC - PricingEngine is hidden behind a feature toggle
@@ -55,7 +57,9 @@ export default class PricingEngine {
private static async _getPricingDefinitions4Entity(tenant: Tenant, entityID: string): Promise<PricingDefinition[]> {
if (entityID) {
const entityIDs = [ entityID ];
- const pricingModelResults = await PricingStorage.getPricingDefinitions(tenant, { entityIDs }, { limit: 1, skip: 0, sort: { createdOn: -1 } });
+ const pricingModelResults = await PricingStorage.getPricingDefinitions(tenant, { entityIDs }, {
+ limit: Constants.DB_RECORD_COUNT_NO_LIMIT, skip: 0, sort: { createdOn: -1 }
+ });
if (pricingModelResults.count > 0) {
return pricingModelResults.result;
}
@@ -64,17 +68,15 @@ export default class PricingEngine {
}
static checkStaticRestrictions(pricingDefinition: PricingDefinition, transaction: Transaction, chargingStation: ChargingStation) : PricingDefinition {
- // -----------------------------------------------------------------------
- // TODO - check here the static restrictions
- // i.e.: - restrictions that are not depending on the actual consumption
- // e.g.: - validity dates, minPowerkW/maxPowerkW
- // -----------------------------------------------------------------------
- if (!PricingEngine.checkConnectorType(pricingDefinition, transaction, chargingStation)
- || !PricingEngine.checkMinPower(pricingDefinition, transaction, chargingStation)
- || !PricingEngine.checkMaxPower(pricingDefinition, transaction, chargingStation)
+ if (pricingDefinition.staticRestrictions) {
+ if (
+ !PricingEngine.checkDateValidity(pricingDefinition.staticRestrictions, transaction)
+ || !PricingEngine.checkConnectorType(pricingDefinition.staticRestrictions, transaction, chargingStation)
+ || !PricingEngine.checkConnectorPower(pricingDefinition.staticRestrictions, transaction, chargingStation)
) {
return null;
}
+ }
// a definition matching the restrictions has been found
return pricingDefinition;
}
@@ -100,30 +102,34 @@ export default class PricingEngine {
return pricingDefinition;
}
- static checkConnectorType(pricingDefinition: PricingDefinition, transaction: Transaction, chargingStation: ChargingStation): boolean {
- if (!Utils.isNullOrUndefined(pricingDefinition.connectorTypes)) {
- const connectorType = Utils.getConnectorFromID(chargingStation, transaction.connectorId)?.type;
- if (!pricingDefinition.connectorTypes.includes(connectorType)) {
+ static checkDateValidity(staticRestrictions: PricingStaticRestriction, transaction: Transaction): boolean {
+ if (!Utils.isNullOrUndefined(staticRestrictions.validFrom)) {
+ if (moment(transaction.timestamp).isBefore(staticRestrictions.validFrom)) {
+ return false;
+ }
+ }
+ if (!Utils.isNullOrUndefined(staticRestrictions.validTo)) {
+ if (moment(transaction.timestamp).isSameOrAfter(staticRestrictions.validTo)) {
return false;
}
}
return true;
}
- static checkMinPower(pricingDefinition: PricingDefinition, transaction: Transaction, chargingStation: ChargingStation): boolean {
- if (!Utils.isNullOrUndefined(pricingDefinition.minOutputPowerkW)) {
- const connectorPowerWatts = Utils.getConnectorFromID(chargingStation, transaction.connectorId)?.power;
- if (Utils.createDecimal(connectorPowerWatts).dividedBy(1000).lessThan(pricingDefinition.minOutputPowerkW)) {
+ static checkConnectorType(staticRestrictions: PricingStaticRestriction, transaction: Transaction, chargingStation: ChargingStation): boolean {
+ if (!Utils.isNullOrUndefined(staticRestrictions.connectorType)) {
+ const connectorType = Utils.getConnectorFromID(chargingStation, transaction.connectorId)?.type;
+ if (staticRestrictions.connectorType !== connectorType) {
return false;
}
}
return true;
}
- static checkMaxPower(pricingDefinition: PricingDefinition, transaction: Transaction, chargingStation: ChargingStation): boolean {
- if (!Utils.isNullOrUndefined(pricingDefinition.maxOutputPowerkW)) {
+ static checkConnectorPower(staticRestrictions: PricingStaticRestriction, transaction: Transaction, chargingStation: ChargingStation): boolean {
+ if (!Utils.isNullOrUndefined(staticRestrictions.connectorPowerkW)) {
const connectorPowerWatts = Utils.getConnectorFromID(chargingStation, transaction.connectorId)?.power;
- if (Utils.createDecimal(connectorPowerWatts).dividedBy(1000).greaterThanOrEqualTo(pricingDefinition.maxOutputPowerkW)) {
+ if (!Utils.createDecimal(connectorPowerWatts).div(1000).equals(staticRestrictions.connectorPowerkW)) {
return false;
}
}

MODIFY
src/server/rest/v1/service/PricingService.ts
src/server/rest/v1/service/PricingService.ts
@@ -151,11 +151,7 @@ export default class PricingService {
pricingDefinition.entityType = filteredRequest.entityType;
pricingDefinition.name = filteredRequest.name;
pricingDefinition.description = filteredRequest.description;
- pricingDefinition.validFrom = filteredRequest.validFrom;
- pricingDefinition.validTo = filteredRequest.validTo;
- pricingDefinition.connectorTypes = filteredRequest.connectorTypes;
- pricingDefinition.minOutputPowerkW = filteredRequest.minOutputPowerkW;
- pricingDefinition.maxOutputPowerkW = filteredRequest.maxOutputPowerkW;
+ pricingDefinition.staticRestrictions = filteredRequest.staticRestrictions;
pricingDefinition.restrictions = filteredRequest.restrictions;
pricingDefinition.dimensions = filteredRequest.dimensions;
pricingDefinition.lastChangedBy = { 'id': req.user.id };

MODIFY
src/server/rest/v1/service/security/PricingSecurity.ts
src/server/rest/v1/service/security/PricingSecurity.ts
@@ -43,12 +43,8 @@ export default class PricingSecurity {
entityType: sanitize(request.entityType),
name: sanitize(request.name),
description: sanitize(request.description),
- connectorTypes: sanitize(request.connectorTypes),
- validFrom: sanitize(request.validFrom),
- validTo: sanitize(request.validTo),
- minOutputPowerkW: sanitize(request.minOutputPowerkW),
- maxOutputPowerkW: sanitize(request.maxOutputPowerkW),
// TODO - not yet sanitized properly
+ staticRestrictions: sanitize(request.staticRestrictions),
restrictions: sanitize(request.restrictions),
dimensions: sanitize(request.dimensions),
} as Partial<PricingDefinition>;

MODIFY
src/storage/mongodb/PricingStorage.ts
src/storage/mongodb/PricingStorage.ts
@@ -25,11 +25,7 @@ export default class PricingStorage {
entityType: pricingDefinition.entityType,
name: pricingDefinition.name,
description: pricingDefinition.description,
- validFrom: Utils.convertToDate(pricingDefinition.validFrom),
- validTo: Utils.convertToDate(pricingDefinition.validTo),
- connectorTypes: pricingDefinition.connectorTypes,
- maxOutputPowerkW: pricingDefinition.maxOutputPowerkW,
- minOutputPowerkW: pricingDefinition.minOutputPowerkW,
+ staticRestrictions: pricingDefinition.staticRestrictions,
restrictions: pricingDefinition.restrictions,
dimensions: pricingDefinition.dimensions,
};

MODIFY
src/types/Pricing.ts
src/types/Pricing.ts
@@ -30,11 +30,7 @@ export default interface PricingDefinition extends CreatedUpdatedProps, Authoriz
entityType: PricingEntity; // Type of the entity this model belongs to
name: string, // Short marketing name - e.g.: BLUE Tariff,
description: string, // A long description to explain it, e.g.: Time-based pricing for low charging stations
- validFrom: Date,
- validTo: Date,
- connectorTypes?: ConnectorType[], // Connector types allowed to use this tariff
- minOutputPowerkW?: number, // Minimum power in kW, for example 0, valid from this charging speed
- maxOutputPowerkW?: number, // Maximum power in kW, for example 20, valid up to this charging speed
+ staticRestrictions?: PricingStaticRestriction,
restrictions?: PricingRestriction,
dimensions: PricingDimensions
}
@@ -45,6 +41,7 @@ export interface PricingDimensions {
chargingTime?: PricingDimension, // Time charging: defined in hours, step_size multiplier: 1 second
parkingTime?: PricingDimension, // Time not charging: defined in hours, step_size multiplier: 1 second
}
+
export interface PricingDimension {
active: boolean, // Lets the user switch OFF that price without loosing the value (if any)
price: number, // Price per unit (excluding VAT) for this tariff dimension
@@ -52,6 +49,13 @@ export interface PricingDimension {
pricedData?: PricedDimensionData // Information set dynamically while charging
}
+export interface PricingStaticRestriction {
+ validFrom?: Date,
+ validTo?: Date,
+ connectorType?: ConnectorType, // Connector types allowed to use this tariff
+ connectorPowerkW?: number
+}
+
export interface PricingRestriction {
// TODO - first Pricing implementation - some options are not yet supported
// startDate?: string, // Start date, for example: 2015-12-24, valid from this day

MODIFY
test/api/BillingStripeTestData.ts
test/api/BillingStripeTestData.ts
@@ -444,10 +444,12 @@ export default class StripeIntegrationTestData {
entityType: null,
name: 'BLUE Tariff',
description: 'Tariff for low EVSE',
- connectorTypes: null,
+ staticRestrictions: {
+ connectorType: null,
+ connectorPowerkW: 40,
validFrom: new Date(),
validTo: moment().add(10, 'minutes').toDate(),
- maxOutputPowerkW: 40000,
+ },
dimensions: {
chargingTime: parkingPrice,
// energy: price4TheEnergy, // do not bill the energy - bill the parking time instead
@@ -469,10 +471,12 @@ export default class StripeIntegrationTestData {
const tariff2: Partial<PricingDefinition> = {
name: 'GREEN Tariff',
description: 'Tariff for fast chargers',
- minOutputPowerkW: 40000,
- connectorTypes: null,
+ staticRestrictions: {
+ connectorType: null,
+ connectorPowerkW: 40,
validFrom: new Date(),
validTo: moment().add(10, 'minutes').toDate(),
+ },
dimensions: {
// chargingTime: parkingPrice, // parking time is free while charging
energy: price4TheEnergy,

MODIFY
test/api/BillingTest.ts
test/api/BillingTest.ts
+/* eslint-disable max-len */
import AsyncTask, { AsyncTaskStatus } from '../../src/types/AsyncTask';
import { BillingChargeInvoiceAction, BillingDataTransactionStop, BillingInvoiceStatus, BillingStatus, BillingUser } from '../../src/types/Billing';
import { BillingSettings, BillingSettingsType, SettingDB } from '../../src/types/Setting';
@@ -93,27 +94,74 @@ class TestData {
assert(!!this.userService, 'User service cannot be null');
}
- public initChargingStationContext() : ChargingStationContext {
+ public async initChargingStationContext() : Promise<ChargingStationContext> {
this.siteContext = this.tenantContext.getSiteContext(ContextDefinition.SITE_CONTEXTS.SITE_WITH_OTHER_USER_STOP_AUTHORIZATION);
this.siteAreaContext = this.siteContext.getSiteAreaContext(ContextDefinition.SITE_AREA_CONTEXTS.WITH_ACL);
this.chargingStationContext = this.siteAreaContext.getChargingStationContext(ContextDefinition.CHARGING_STATION_CONTEXTS.ASSIGNED_OCPP16);
assert(!!this.chargingStationContext, 'Charging station context should not be null');
- return this.chargingStationContext;
+ // -------------------------------------------------
+ // No pricing definition here!
+ // -------------------------------------------------
+ // await this.createTariff4ChargingStation(this.chargingStationContext.getChargingStation());
+ return Promise.resolve(this.chargingStationContext);
}
- public initChargingStationContext2TestChargingTime() : ChargingStationContext {
+ public async initChargingStationContext2TestChargingTime() : Promise<ChargingStationContext> {
this.siteContext = this.tenantContext.getSiteContext(ContextDefinition.SITE_CONTEXTS.SITE_BASIC);
this.siteAreaContext = this.siteContext.getSiteAreaContext(ContextDefinition.SITE_AREA_CONTEXTS.WITH_SMART_CHARGING_THREE_PHASED);
this.chargingStationContext = this.siteAreaContext.getChargingStationContext(ContextDefinition.CHARGING_STATION_CONTEXTS.ASSIGNED_OCPP16 + '-' + ContextDefinition.SITE_CONTEXTS.SITE_BASIC + '-' + ContextDefinition.SITE_AREA_CONTEXTS.WITH_SMART_CHARGING_THREE_PHASED + '-singlePhased');
assert(!!this.chargingStationContext, 'Charging station context should not be null');
+ await this.createTariff4ChargingStation(this.chargingStationContext.getChargingStation(), {
+ flatFee: {
+ price: 1,
+ active: true
+ },
+ chargingTime: {
+ price: 0.4,
+ active: true
+ }
+ });
return this.chargingStationContext;
}
- public initChargingStationContext2EnergyPlusFlatFee() : ChargingStationContext {
+ public async initChargingStationContext2TestEnergyPlusFlatFee() : Promise<ChargingStationContext> {
this.siteContext = this.tenantContext.getSiteContext(ContextDefinition.SITE_CONTEXTS.SITE_BASIC);
this.siteAreaContext = this.siteContext.getSiteAreaContext(ContextDefinition.SITE_AREA_CONTEXTS.WITH_SMART_CHARGING_THREE_PHASED);
this.chargingStationContext = this.siteAreaContext.getChargingStationContext(ContextDefinition.CHARGING_STATION_CONTEXTS.ASSIGNED_OCPP16 + '-' + ContextDefinition.SITE_CONTEXTS.SITE_BASIC + '-' + ContextDefinition.SITE_AREA_CONTEXTS.WITH_SMART_CHARGING_THREE_PHASED);
assert(!!this.chargingStationContext, 'Charging station context should not be null');
+ await this.createTariff4ChargingStation(this.chargingStationContext.getChargingStation(), {
+ flatFee: {
+ price: 2,
+ active: true
+ },
+ energy: {
+ price: 0.25,
+ active: true
+ },
+ chargingTime: {
+ price: 0.4,
+ active: false // THIS IS OFF
+ }
+ });
+ return this.chargingStationContext;
+ }
+
+ public async initChargingStationContext2TestFastCharger() : Promise<ChargingStationContext> {
+ this.siteContext = this.tenantContext.getSiteContext(ContextDefinition.SITE_CONTEXTS.SITE_BASIC);
+ this.siteAreaContext = this.siteContext.getSiteAreaContext(ContextDefinition.SITE_AREA_CONTEXTS.WITH_SMART_CHARGING_DC);
+ this.chargingStationContext = this.siteAreaContext.getChargingStationContext(ContextDefinition.CHARGING_STATION_CONTEXTS.ASSIGNED_OCPP16 + '-' + ContextDefinition.SITE_CONTEXTS.SITE_BASIC + '-' + ContextDefinition.SITE_AREA_CONTEXTS.WITH_SMART_CHARGING_DC);
+ assert(!!this.chargingStationContext, 'Charging station context should not be null');
+ await this.createTariff4ChargingStation(this.chargingStationContext.getChargingStation(), {
+ chargingTime: {
+ price: 0.16,
+ active: true
+ },
+ energy: {
+ price: 0.50,
+ active: true
+ }
+ }, ConnectorType.COMBO_CCS);
+
return this.chargingStationContext;
}
@@ -288,40 +336,40 @@ class TestData {
// assert(selectedSites[0], 'The Site should not be null');
// await this.createTariff4Site(selectedSites[0].id);
- const chargingStations: ChargingStation[] = (await this.adminUserService.chargingStationApi.readAll({}, { limit: 0, skip: 0 }))?.data?.result;
- let selectedChargingStations = chargingStations.filter((chargingStation) => chargingStation.id === 'cs-16-ut-site-withSmartChargingDC');
- await this.createTariff4ChargingStation(selectedChargingStations[0]);
-
- selectedChargingStations = chargingStations.filter((chargingStation) => chargingStation.id === 'cs-16-ut-site-withSmartChargingSinglePhased');
- await this.createTariff4ChargingStation(selectedChargingStations[0]);
-
- selectedChargingStations = chargingStations.filter((chargingStation) => chargingStation.id === 'cs-16-ut-site-withSmartChargingThreePhased');
- await this.createTariff4ChargingStation(selectedChargingStations[0], {
- flatFee: {
- price: 2,
- active: true
- },
- energy: {
- price: 0.25,
- active: true
- },
- chargingTime: {
- price: 0.4,
- active: false // THIS IS OFF
- }
- });
-
- selectedChargingStations = chargingStations.filter((chargingStation) => chargingStation.id === 'cs-16-ut-site-withSmartChargingThreePhased-singlePhased');
- await this.createTariff4ChargingStation(selectedChargingStations[0], {
- flatFee: {
- price: 1,
- active: true
- },
- chargingTime: {
- price: 0.4,
- active: true
- }
- });
+ // const chargingStations: ChargingStation[] = (await this.adminUserService.chargingStationApi.readAll({}, { limit: 0, skip: 0 }))?.data?.result;
+ // let selectedChargingStations = chargingStations.filter((chargingStation) => chargingStation.id === 'cs-16-ut-site-withSmartChargingDC');
+ // await this.createTariff4ChargingStation(selectedChargingStations[0], null, ConnectorType.COMBO_CCS);
+
+ // selectedChargingStations = chargingStations.filter((chargingStation) => chargingStation.id === 'cs-16-ut-site-withSmartChargingSinglePhased');
+ // await this.createTariff4ChargingStation(selectedChargingStations[0]);
+
+ // selectedChargingStations = chargingStations.filter((chargingStation) => chargingStation.id === 'cs-16-ut-site-withSmartChargingThreePhased');
+ // await this.createTariff4ChargingStation(selectedChargingStations[0], {
+ // flatFee: {
+ // price: 2,
+ // active: true
+ // },
+ // energy: {
+ // price: 0.25,
+ // active: true
+ // },
+ // chargingTime: {
+ // price: 0.4,
+ // active: false // THIS IS OFF
+ // }
+ // });
+
+ // selectedChargingStations = chargingStations.filter((chargingStation) => chargingStation.id === 'cs-16-ut-site-withSmartChargingThreePhased-singlePhased');
+ // await this.createTariff4ChargingStation(selectedChargingStations[0], {
+ // flatFee: {
+ // price: 1,
+ // active: true
+ // },
+ // chargingTime: {
+ // price: 0.4,
+ // active: true
+ // }
+ // });
}
public async createTariff4Company(companyID: string): Promise<void> {
@@ -330,7 +378,9 @@ class TestData {
entityType: PricingEntity.COMPANY,
name: 'GREEN Tariff',
description: 'Tariff for slow chargers',
- maxOutputPowerkW: 40,
+ staticRestrictions: {
+ connectorPowerkW: 40,
+ },
dimensions: {
flatFee: {
price: 1.25,
@@ -358,12 +408,6 @@ class TestData {
const pricingDefinitionId = response?.data?.id;
response = await this.adminUserService.pricingApi.readPricingDefinition(pricingDefinitionId);
assert(response?.data?.id === pricingDefinitionId, 'The ID should be: ' + pricingDefinitionId);
-
-
- const pricingDefinition = response?.data;
- pricingDefinition.pricingDefinitions = [tariff];
- response = await this.adminUserService.pricingApi.updatePricingDefinition(pricingDefinition);
- assert(response?.data?.status === 'Success', 'The operation should succeed');
}
public async createTariff4Site(siteID: string): Promise<void> {
@@ -372,7 +416,9 @@ class TestData {
entityType: PricingEntity.SITE,
name: 'RED Tariff',
description: 'Tariff for fast chargers',
- minOutputPowerkW: 40,
+ staticRestrictions: {
+ connectorPowerkW: 40,
+ },
dimensions: {
flatFee: {
price: 2.25,
@@ -400,44 +446,22 @@ class TestData {
const pricingDefinitionId = response?.data?.id;
response = await this.adminUserService.pricingApi.readPricingDefinition(pricingDefinitionId);
assert(response?.data?.id === pricingDefinitionId, 'The ID should be: ' + pricingDefinitionId);
-
- const pricingDefinition = response?.data;
- pricingDefinition.pricingDefinitions = [tariff];
- response = await this.adminUserService.pricingApi.updatePricingDefinition(pricingDefinition);
- assert(response?.data?.status === 'Success', 'The operation should succeed');
}
- public async createTariff4ChargingStation(chargingStation: ChargingStation, dimensions: PricingDimensions = null, connectorTypes: ConnectorType[] = null): Promise<void> {
- dimensions = dimensions || {
- flatFee: {
- price: 1.25,
- active: true
- },
- chargingTime: {
- price: 0.15,
- active: true
- },
- energy: {
- price: 0.35,
- active: true
- },
- parkingTime: {
- price: 0.75,
- active: true
- },
- };
-
+ public async createTariff4ChargingStation(chargingStation: ChargingStation, dimensions: PricingDimensions, connectorType: ConnectorType = null): Promise<void> {
// Set a default value
- connectorTypes = connectorTypes || [ ConnectorType.TYPE_2 ];
+ connectorType = connectorType || ConnectorType.TYPE_2;
const tariff: Partial<PricingDefinition> = {
entityID: chargingStation.id, // a pricing model for the site
entityType: PricingEntity.CHARGING_STATION,
name: 'CS Tariff - ' + chargingStation.id,
description: 'Tariff for CS -' + chargingStation.id,
- connectorTypes,
+ staticRestrictions: {
+ connectorType,
validFrom: new Date(),
- validTo: moment().add(10, 'minutes').toDate(),
+ validTo: moment().add(10, 'minutes').toDate()
+ },
dimensions
};
@@ -449,10 +473,33 @@ class TestData {
response = await this.adminUserService.pricingApi.readPricingDefinition(pricingDefinitionId);
assert(response?.data?.id === pricingDefinitionId, 'The ID should be: ' + pricingDefinitionId);
- const pricingDefinition = response?.data;
- pricingDefinition.pricingDefinitions = [tariff];
- response = await this.adminUserService.pricingApi.updatePricingDefinition(pricingDefinition);
+ // Create a 2nd one valid in the future with a stupid flat fee
+ tariff.name = tariff.name + ' - In the future';
+ tariff.staticRestrictions = {
+ connectorType,
+ validFrom: moment().add(10, 'years').toDate(),
+ },
+ tariff.dimensions.flatFee = {
+ active: true,
+ price: 111
+ };
+ response = await this.adminUserService.pricingApi.createPricingDefinition(tariff);
+ assert(response?.data?.status === 'Success', 'The operation should succeed');
+ assert(response?.data?.id, 'The ID should not be null');
+
+ // Create a 3rd one valid in the past
+ tariff.name = tariff.name + ' - In the past';
+ tariff.staticRestrictions = {
+ connectorType,
+ validTo: moment().add(-1, 'hours').toDate(),
+ },
+ tariff.dimensions.flatFee = {
+ active: true,
+ price: 222
+ };
+ response = await this.adminUserService.pricingApi.createPricingDefinition(tariff);
assert(response?.data?.status === 'Success', 'The operation should succeed');
+ assert(response?.data?.id, 'The ID should not be null');
}
}
@@ -639,7 +686,7 @@ describe('Billing Service', function() {
describe('with Transaction Billing ON', () => {
before(async () => {
// Initialize the charing station context
- testData.initChargingStationContext();
+ await testData.initChargingStationContext();
// Initialize the Billing module
testData.billingImpl = await testData.setBillingSystemValidCredentials();
// Make sure the required users are in sync
@@ -1036,7 +1083,7 @@ describe('Billing Service', function() {
describe('with Transaction Billing OFF', () => {
before(async () => {
expect(testData.userContext).to.not.be.null;
- testData.initChargingStationContext();
+ await testData.initChargingStationContext();
// Initialize the Billing module
testData.billingImpl = await testData.setBillingSystemValidCredentials(false);
});
@@ -1070,11 +1117,11 @@ describe('Billing Service', function() {
testData.billingImpl = await testData.setBillingSystemValidCredentials(true, true /* immediateBillingAllowed ON */);
});
- describe('Where admin user', () => {
+ describe('FF + CT', () => {
// eslint-disable-next-line @typescript-eslint/require-await
before(async () => {
// Initialize the charing station context
- testData.initChargingStationContext2TestChargingTime();
+ await testData.initChargingStationContext2TestChargingTime();
});
it('should create and bill an invoice with FF + CT', async () => {
@@ -1089,11 +1136,11 @@ describe('Billing Service', function() {
});
- describe('Where admin user', () => {
+ describe('FF + ENERGY', () => {
// eslint-disable-next-line @typescript-eslint/require-await
before(async () => {
// Initialize the charing station context
- testData.initChargingStationContext2EnergyPlusFlatFee();
+ await testData.initChargingStationContext2TestEnergyPlusFlatFee();
});
it('should create and bill an invoice with FF + ENERGY', async () => {
@@ -1107,13 +1154,30 @@ describe('Billing Service', function() {
});
});
+
+ describe('On COMBO CCS - DC', () => {
+ // eslint-disable-next-line @typescript-eslint/require-await
+ before(async () => {
+ // Initialize the charing station context
+ await testData.initChargingStationContext2TestFastCharger();
+ });
+
+ it('should create and bill an invoice on COMBO CCS - DC', async () => {
+ await testData.userService.billingApi.forceSynchronizeUser({ id: testData.userContext.id });
+ const userWithBillingData = await testData.billingImpl.getUser(testData.userContext);
+ await testData.assignPaymentMethod(userWithBillingData, 'tok_fr');
+ const transactionID = await testData.generateTransaction(testData.userContext);
+ assert(transactionID, 'transactionID should not be null');
+ // Check that we have a new invoice with an invoiceID and an invoiceNumber
+ await testData.checkTransactionBillingData(transactionID, BillingInvoiceStatus.PAID);
+ });
+
+ });
});
describe('with Transaction Billing + Periodic Billing ON', () => {
before(async () => {
testData.initUserContextAsAdmin();
- // Initialize the charing station context
- testData.initChargingStationContext2TestChargingTime();
// Initialize the Billing module
testData.billingImpl = await testData.setBillingSystemValidCredentials(true, false /* immediateBillingAllowed OFF, so periodicBilling ON */);
});
@@ -1121,6 +1185,8 @@ describe('Billing Service', function() {
describe('Where admin user', () => {
// eslint-disable-next-line @typescript-eslint/require-await
before(async () => {
+ // Initialize the charing station context
+ await testData.initChargingStationContext2TestChargingTime();
});
it('should create a DRAFT invoice, Finalize it and Pay it', async () => {

pricing - static restriction - check date validity