pricing - restrictions - checkDayOfWeek
MODIFY
src/assets/schemas/pricing/pricing-definition.json
src/assets/schemas/pricing/pricing-definition.json
"type": "string",
"sanitize": "mongo"
},
+ "dayOfWeek": {
+ "type": "number",
+ "sanitize": "mongo",
+ "enum": [ 1, 2, 3, 4, 5, 6, 7]
+ },
"staticRestrictions": {
"type": "object",
"nullable": true,
"type": "object",
"nullable": true,
"properties": {
+ "daysOfWeek": {
+ "type": "array",
+ "items": {
+ "$ref": "pricing-definition#/definitions/dayOfWeek"
+ } ,
+ "nullable": true
+ },
"minEnergyKWh": {
"type": "number",
"sanitize": "mongo"

MODIFY
src/integration/pricing/ConsumptionPricer.ts
src/integration/pricing/ConsumptionPricer.ts
@@ -41,15 +41,14 @@ export default class ConsumptionPricer {
private checkPricingDefinitionRestrictions(pricingDefinition: ResolvedPricingDefinition) : ResolvedPricingDefinition {
if (pricingDefinition.restrictions) {
- if (!this.checkMinEnergy(pricingDefinition.restrictions)
+ if (!this.checkDayOfWeek(pricingDefinition.restrictions)
+ || !this.checkMinEnergy(pricingDefinition.restrictions)
|| !this.checkMaxEnergy(pricingDefinition.restrictions)
|| !this.checkMinDuration(pricingDefinition.restrictions)
|| !this.checkMaxDuration(pricingDefinition.restrictions)) {
// ---------------------------------------------------
// TODO - additional restrictions may be checked here
// ---------------------------------------------------
- // 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
// startDate?: string, // Start date, for example: 2015-12-24, valid from this day
// endDate?: string, // End date, for example: 2015-12-27, valid until this day (excluding this day)
// daysOfWeek?: DayOfWeek[], // Which day(s) of the week this tariff is valid
@@ -60,6 +59,15 @@ export default class ConsumptionPricer {
return pricingDefinition;
}
+ private checkDayOfWeek(restrictions: PricingRestriction): boolean {
+ if (!Utils.isNullOrUndefined(restrictions.daysOfWeek)) {
+ // ACHTUNG - moment.isoWeekday() - 1:MONDAY, 7: SUNDAY
+ return restrictions.daysOfWeek.includes(moment(this.consumptionData.startedAt).isoWeekday());
+ }
+ // No restrictions related to the day of the week
+ 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
@@ -77,11 +77,9 @@ export interface PricingStaticRestriction {
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
- // endDate?: string, // End date, for example: 2015-12-27, valid until this day (excluding this day)
// 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
+ daysOfWeek?: DayOfWeek[], // Which day(s) of the week this tariff is valid
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
@@ -772,8 +772,20 @@ describe('Billing', function() {
await billingTestHelper.checkTransactionBillingData(transactionID, BillingInvoiceStatus.PAID, 17.37);
});
- });
+ it('should bill an invoice taking the Day of the week into account', async () => {
+ await billingTestHelper.initChargingStationContext2TestFastCharger('TODAY');
+ await billingTestHelper.initChargingStationContext2TestFastCharger('OTHER_DAYS');
+ // 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, 33.82);
+ });
+ });
describe('When basic user has a free access', () => {
// eslint-disable-next-line @typescript-eslint/require-await

MODIFY
test/api/BillingTestHelper.ts
test/api/BillingTestHelper.ts
@@ -4,7 +4,7 @@ import { BillingDataTransactionStop, BillingInvoice, BillingInvoiceStatus, Billi
import { BillingSettings, BillingSettingsType, SettingDB } from '../../src/types/Setting';
import ChargingStation, { ConnectorType } from '../../src/types/ChargingStation';
import FeatureToggles, { Feature } from '../../src/utils/FeatureToggles';
-import PricingDefinition, { PricingDimension, PricingDimensions, PricingEntity, PricingRestriction } from '../../src/types/Pricing';
+import PricingDefinition, { DayOfWeek, PricingDimension, PricingDimensions, PricingEntity, PricingRestriction } from '../../src/types/Pricing';
import chai, { assert, expect } from 'chai';
import AsyncTaskStorage from '../../src/storage/mongodb/AsyncTaskStorage';
@@ -257,6 +257,35 @@ export default class BillingTestHelper {
active: true
}
};
+ } else if (testMode === 'TODAY') {
+ dimensions = {
+ flatFee: {
+ price: 1.5, // Euro
+ active: true
+ },
+ energy: {
+ price: 1,
+ active: true
+ }
+ };
+ restrictions = {
+ daysOfWeek: [ moment().isoWeekday() ] // Sets today as the only day allowed for this pricing definition
+ };
+ } else if (testMode === 'OTHER_DAYS') {
+ dimensions = {
+ flatFee: {
+ price: 666, // Euro
+ active: true
+ },
+ energy: {
+ price: 666,
+ active: true
+ }
+ };
+ restrictions = {
+ // 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 {
dimensions = {
energy: {
@@ -518,11 +547,12 @@ export default class BillingTestHelper {
// Set a default value
connectorType = connectorType || ConnectorType.TYPE_2;
+ const tariffName = testMode;
const tariff: Partial<PricingDefinition> = {
entityID: chargingStation.id, // a pricing model for the site
entityType: PricingEntity.CHARGING_STATION,
- name: testMode,
- description: 'Tariff for CS ' + chargingStation.id + ' - ' + testMode + ' - ' + connectorType,
+ name: tariffName,
+ description: 'Tariff for CS ' + chargingStation.id + ' - ' + tariffName + ' - ' + connectorType,
staticRestrictions: {
connectorType,
validFrom: new Date(),
@@ -542,7 +572,7 @@ export default class BillingTestHelper {
assert(response?.data?.entityName === chargingStation.id);
// Create a 2nd one valid in the future with a stupid flat fee
- tariff.name = tariff.name + ' - In the future';
+ tariff.name = tariffName + ' - In the future';
tariff.staticRestrictions = {
connectorType,
validFrom: moment().add(10, 'years').toDate(),
@@ -556,7 +586,7 @@ export default class BillingTestHelper {
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.name = tariffName + ' - In the past';
tariff.staticRestrictions = {
connectorType,
validTo: moment().add(-1, 'hours').toDate(),

pricing - restrictions - checkDayOfWeek