pricing - handle consumption gaps + tests
MODIFY
src/integration/pricing/PricingEngine.ts
src/integration/pricing/PricingEngine.ts
/* eslint-disable max-len */
import FeatureToggles, { Feature } from '../../utils/FeatureToggles';
-import PricingDefinition, { PricedConsumptionData, PricingEntity, PricingStaticRestriction, ResolvedPricingDefinition, ResolvedPricingModel } from '../../types/Pricing';
+import PricingDefinition, { DimensionType, PricedConsumptionData, PricingDimensions, PricingEntity, PricingStaticRestriction, ResolvedPricingDefinition, ResolvedPricingModel } from '../../types/Pricing';
import ChargingStation from '../../types/ChargingStation';
import Constants from '../../utils/Constants';
@@ -8,6 +8,7 @@ import Consumption from '../../types/Consumption';
import ConsumptionPricer from './ConsumptionPricer';
import Logging from '../../utils/Logging';
import LoggingHelper from '../../utils/LoggingHelper';
+import PricingHelper from './PricingHelper';
import PricingStorage from '../../storage/mongodb/PricingStorage';
import { ServerAction } from '../../types/Server';
import Tenant from '../../types/Tenant';
@@ -61,7 +62,12 @@ export default class PricingEngine {
public static priceConsumption(tenant: Tenant, pricingModel: ResolvedPricingModel, consumptionData: Consumption): PricedConsumptionData {
const consumptionPricer = new ConsumptionPricer(tenant, pricingModel, consumptionData);
- return consumptionPricer.priceConsumption();
+ const pricedConsumptionData = consumptionPricer.priceConsumption();
+ const consumptionWh = consumptionData?.consumptionWh || 0;
+ if (consumptionWh > 0) {
+ PricingEngine.withdrawParkingTimeConsumption(pricingModel);
+ }
+ return pricedConsumptionData;
}
public static extractFinalPricingData(pricingModel: ResolvedPricingModel): PricedConsumptionData[] {
@@ -188,4 +194,22 @@ export default class PricingEngine {
// Nothing to bill for the current pricing definition
return null;
}
+
+ private static withdrawParkingTimeConsumption(pricingModel: ResolvedPricingModel): void {
+ const allDimensionsWithParkingTime: PricingDimensions[] = [];
+ pricingModel.pricingDefinitions.forEach((pricingDefinition) => {
+ if (pricingDefinition.dimensions.parkingTime) {
+ allDimensionsWithParkingTime.push(pricingDefinition.dimensions);
+ }
+ });
+ // Determine the already priced parking time
+ const pricedParkingTime = PricingHelper.accumulatePricingDimension(allDimensionsWithParkingTime, DimensionType.PARKING_TIME);
+ if (pricedParkingTime.cumulatedAmount > 0) {
+ // Let's withdraw it
+ allDimensionsWithParkingTime.forEach((pricingDimension) => {
+ // Reset the parking time dimension
+ delete pricingDimension[DimensionType.PARKING_TIME].pricedData;
+ });
+ }
+ }
}

MODIFY
test/api/BillingTest.ts
test/api/BillingTest.ts
@@ -703,7 +703,7 @@ describe('Billing', function() {
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, 6.49);
+ await billingTestHelper.checkTransactionBillingData(transactionID, BillingInvoiceStatus.PAID, 5.66);
});
it('should bill the CT(STEP)+PT(STEP) on COMBO CCS - DC', async () => {
@@ -714,7 +714,7 @@ describe('Billing', function() {
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, 11.00);
+ await billingTestHelper.checkTransactionBillingData(transactionID, BillingInvoiceStatus.PAID, 10.00);
});
it('should bill the ENERGY + PT(STEP) on COMBO CCS - DC', async () => {
@@ -725,14 +725,14 @@ describe('Billing', function() {
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, 19.49);
+ await billingTestHelper.checkTransactionBillingData(transactionID, BillingInvoiceStatus.PAID, 18.16);
});
it('should bill the FF+E with 2 tariffs on COMBO CCS - DC', async () => {
// A first Tariff for the ENERGY Only
await billingTestHelper.initChargingStationContext2TestFastCharger('FF+E');
// A second Tariff applied after 30 mins!
- await billingTestHelper.initChargingStationContext2TestFastCharger('E-After30mins');
+ await billingTestHelper.initChargingStationContext2TestFastCharger('E-After30mins+PT');
// A tariff applied immediately
await billingTestHelper.userService.billingApi.forceSynchronizeUser({ id: billingTestHelper.userContext.id });
const userWithBillingData = await billingTestHelper.billingImpl.getUser(billingTestHelper.userContext);
@@ -740,7 +740,7 @@ describe('Billing', function() {
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, 24.63);
+ await billingTestHelper.checkTransactionBillingData(transactionID, BillingInvoiceStatus.PAID, 22.96);
});
it('should bill the FF+E(STEP)+E(STEP) with 2 tariffs on COMBO CCS - DC', async () => {

MODIFY
test/api/BillingTestHelper.ts
test/api/BillingTestHelper.ts
@@ -207,7 +207,7 @@ export default class BillingTestHelper {
active: true
}
};
- } else if (testMode === 'E-After30mins') {
+ } else if (testMode === 'E-After30mins+PT') {
// Create a second tariff with a different pricing strategy
dimensions = {
energy: {

pricing - handle consumption gaps + tests