pricing - restrictions - checkTimeValidity
MODIFY
src/assets/schemas/pricing/pricing-definition.json
src/assets/schemas/pricing/pricing-definition.json
} ,
"nullable": true
},
+ "timeFrom": {
+ "type": "string",
+ "sanitize": "mongo",
+ "pattern": "^(0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]$"
+ },
+ "timeTo": {
+ "type": "string",
+ "sanitize": "mongo",
+ "pattern": "^(0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]$"
+ },
"minEnergyKWh": {
"type": "number",
"sanitize": "mongo"

MODIFY
src/integration/pricing/ConsumptionPricer.ts
src/integration/pricing/ConsumptionPricer.ts
@@ -42,6 +42,7 @@ export default class ConsumptionPricer {
private checkPricingDefinitionRestrictions(pricingDefinition: ResolvedPricingDefinition) : ResolvedPricingDefinition {
if (pricingDefinition.restrictions) {
if (!this.checkDayOfWeek(pricingDefinition.restrictions)
+ || !this.checkTimeValidity(pricingDefinition.restrictions)
|| !this.checkMinEnergy(pricingDefinition.restrictions)
|| !this.checkMaxEnergy(pricingDefinition.restrictions)
|| !this.checkMinDuration(pricingDefinition.restrictions)
@@ -62,6 +63,25 @@ export default class ConsumptionPricer {
return true;
}
+
+ private checkTimeValidity(restrictions: PricingRestriction): boolean {
+ if (!Utils.isNullOrUndefined(restrictions.timeFrom)) {
+ const hours = Utils.convertToInt(restrictions.timeFrom.slice(0, 2));
+ const minutes = Utils.convertToInt(restrictions.timeFrom.slice(3));
+ if (moment(this.consumptionData.startedAt).isBefore(new Date().setHours(hours, minutes))) {
+ return false;
+ }
+ }
+ if (!Utils.isNullOrUndefined(restrictions.timeTo)) {
+ const hours = Utils.convertToInt(restrictions.timeTo.slice(0, 2));
+ const minutes = Utils.convertToInt(restrictions.timeTo.slice(3));
+ if (moment(this.consumptionData.startedAt).isSameOrAfter(new Date().setHours(hours, minutes))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
private checkMinEnergy(restrictions: PricingRestriction): boolean {
if (!Utils.isNullOrUndefined(restrictions.minEnergyKWh)) {
if (Utils.createDecimal(this.consumptionData.cumulatedConsumptionWh).div(1000).lessThan(restrictions.minEnergyKWh)) {

MODIFY
src/types/Pricing.ts
src/types/Pricing.ts
@@ -76,10 +76,9 @@ export interface PricingStaticRestriction {
}
export interface PricingRestriction {
- // TODO - first Pricing implementation - some options are not yet supported
- // startTime?: string, // Start time of day, for example 13:30, valid from this time of the day. Must be in 24h format with leading zeros. Hour/Minute se
- // endTime?: string, // End time of day, for example 19:45, valid until this time of the day. Same syntax as start_time
daysOfWeek?: DayOfWeek[], // Which day(s) of the week this tariff is valid
+ timeFrom?: string, // Valid from this time of the day
+ timeTo?: string, // Valid until this time of the day
minEnergyKWh?: number; // Minimum used energy in kWh, for example 20, valid from this amount of energy is used
maxEnergyKWh?: number; // Maximum used energy in kWh, for example 50, valid until this amount of energy is used
minDurationSecs?: number; // Minimum duration in seconds, valid for a duration from x seconds

MODIFY
test/api/BillingTest.ts
test/api/BillingTest.ts
@@ -785,6 +785,19 @@ describe('Billing', function() {
// Check that we have a new invoice with an invoiceID and an invoiceNumber
await billingTestHelper.checkTransactionBillingData(transactionID, BillingInvoiceStatus.PAID, 33.82);
});
+
+ it('should bill an invoice taking the Time into account', async () => {
+ await billingTestHelper.initChargingStationContext2TestFastCharger('NEXT_HOUR');
+ await billingTestHelper.initChargingStationContext2TestFastCharger('THIS_HOUR');
+ // A tariff applied immediately
+ await billingTestHelper.userService.billingApi.forceSynchronizeUser({ id: billingTestHelper.userContext.id });
+ const userWithBillingData = await billingTestHelper.billingImpl.getUser(billingTestHelper.userContext);
+ await billingTestHelper.assignPaymentMethod(userWithBillingData, 'tok_fr');
+ const transactionID = await billingTestHelper.generateTransaction(billingTestHelper.userContext);
+ assert(transactionID, 'transactionID should not be null');
+ // Check that we have a new invoice with an invoiceID and an invoiceNumber
+ await billingTestHelper.checkTransactionBillingData(transactionID, BillingInvoiceStatus.PAID, 55.22);
+ });
});
describe('When basic user has a free access', () => {

MODIFY
test/api/BillingTestHelper.ts
test/api/BillingTestHelper.ts
@@ -286,6 +286,30 @@ export default class BillingTestHelper {
// Sets all other days as the days allowed for this pricing definition
daysOfWeek: [ DayOfWeek.MONDAY, DayOfWeek.TUESDAY, DayOfWeek.WEDNESDAY, DayOfWeek.THURSDAY, DayOfWeek.FRIDAY, DayOfWeek.SATURDAY, DayOfWeek.SUNDAY ].filter((day) => day !== moment().isoWeekday())
};
+ } else if (testMode === 'THIS_HOUR') {
+ dimensions = {
+ energy: {
+ price: 3,
+ active: true
+ }
+ };
+ restrictions = {
+ daysOfWeek: [ moment().isoWeekday() ], // Sets today as the only day allowed for this pricing definition
+ timeFrom: moment().format('HH:mm'), // From this hour
+ timeTo: moment().add(30, 'minutes').format('HH:mm'), // Validity for half an hour
+ };
+ } else if (testMode === 'NEXT_HOUR') {
+ dimensions = {
+ chargingTime: {
+ price: 5, // Euro per hour
+ active: true
+ },
+ };
+ restrictions = {
+ daysOfWeek: [ moment().isoWeekday() ], // Sets today as the only day allowed for this pricing definition
+ timeFrom: moment().add(30, 'minutes').format('HH:mm'), // Valid in half an hour
+ timeTo: moment().add(30 + 60, 'minutes').format('HH:mm'), // for one hour
+ };
} else {
dimensions = {
energy: {

pricing - restrictions - checkTimeValidity