billing/princing - some test refactoring/cleanup
MODIFY
src/integration/pricing/PricingEngine.ts
src/integration/pricing/PricingEngine.ts
@@ -13,7 +13,6 @@ import Utils from '../../utils/Utils';
export default class PricingEngine {
static async resolvePricingContext(tenant: Tenant, transaction: Transaction): Promise<ResolvedPricingModel> {
-
// -----------------------------------------------------------------------------------------
// TODO - We need to find the pricing model to apply by resolving the hierarchy of contexts
// that may override (or extend) the pricing definitions.
@@ -104,14 +103,14 @@ export default class PricingEngine {
// Search for the first pricing definition matching the current dimension type and the pricing restrictions if any!
const activePricingDefinitions = pricingModel.pricingDefinitions.filter((pricingDefinition) =>
// We search for a pricing definition where the current dimension exists
- PricingEngine.checkPricingRestrictions(pricingDefinition, dimensionType)
+ PricingEngine.checkPricingDimensionRestrictions(pricingDefinition, dimensionType)
);
// Iterate throw the list of pricing definitions where the current dimension makes sense
let pricingDimensionData: PricingDimensionData = null;
for (const activePricingDefinition of activePricingDefinitions) {
const dimensionToPrice = activePricingDefinition.dimensions[dimensionType];
if (dimensionToPrice) {
- pricingDimensionData = PricingEngine.applyPricingDefinition(dimensionToPrice, quantity);
+ pricingDimensionData = PricingEngine.priceDimension(dimensionToPrice, quantity);
if (pricingDimensionData) {
// TODO - clarify where to show the actual tariff name
pricingDimensionData.itemDescription = activePricingDefinition.name;
@@ -122,18 +121,15 @@ export default class PricingEngine {
return pricingDimensionData;
}
- static checkPricingRestrictions(pricingDefinition: PricingDefinition, dimensionType: string) : PricingDefinition {
+ static checkPricingDimensionRestrictions(pricingDefinition: PricingDefinition, dimensionType: string) : PricingDefinition {
const pricingDimension: PricingDimension = pricingDefinition.dimensions[dimensionType];
if (pricingDimension?.active) {
return pricingDefinition;
}
- // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- // TODO - check for pricing restriction and power thresholds
- // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
return null;
}
- static applyPricingDefinition(pricingDimension: PricingDimension, quantity: number): PricingDimensionData {
+ static priceDimension(pricingDimension: PricingDimension, quantity: number): PricingDimensionData {
let amount: number;
if (pricingDimension.stepSize) {

MODIFY
test/api/BillingStripeIntegrationTest.ts
test/api/BillingStripeIntegrationTest.ts
@@ -23,14 +23,14 @@ describe('Billing Stripe Service', function() {
await testData.initialize();
});
- describe('Initialization Pricing Model', () => {
+ xdescribe('Initialization Pricing Model', () => {
before(async () => {
});
after(async () => {
});
- it('check CRUD operations on a Pricing Model', async () => {
+ xit('check CRUD operations on a Pricing Model', async () => {
await testData.checkPricingModel();
});
});

MODIFY
test/api/BillingTest.ts
test/api/BillingTest.ts
@@ -2,6 +2,7 @@ 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';
import FeatureToggles, { Feature } from '../../src/utils/FeatureToggles';
+import PricingModel, { PricingDefinition, PricingDimension, PricingRestriction } from '../../src/types/Pricing';
import chai, { assert, expect } from 'chai';
import AsyncTaskStorage from '../../src/storage/mongodb/AsyncTaskStorage';
@@ -53,6 +54,15 @@ class TestData {
public billingImpl: StripeBillingIntegration;
public billingUser: BillingUser; // DO NOT CONFUSE - BillingUser is not a User!
+ public async initialize() {
+ this.tenantContext = await ContextProvider.defaultInstance.getTenantContext(ContextDefinition.TENANT_CONTEXTS.TENANT_BILLING);
+ this.adminUserContext = this.tenantContext.getUserContext(ContextDefinition.USER_CONTEXTS.DEFAULT_ADMIN);
+ this.adminUserService = new CentralServerService(
+ this.tenantContext.getTenant().subdomain,
+ this.adminUserContext
+ );
+ }
+
public async assignPaymentMethod(user: BillingUser, stripe_test_token: string) : Promise<Stripe.CustomerSource> {
// Assign a source using test tokens (instead of test card numbers)
// c.f.: https://stripe.com/docs/testing#cards
@@ -232,6 +242,59 @@ class TestData {
const draftInvoice = await this.getLatestDraftInvoice(userId);
return (draftInvoice) ? draftInvoice.sessions?.length : 0;
}
+
+ public async checkPricingModel(): Promise<void> {
+ const initialPricingModel: Partial<PricingModel> = {
+ contextID: null, // a pricing model for the tenant
+ pricingDefinitions: []
+ };
+ let response = await this.adminUserService.pricingApi.createPricingModel(initialPricingModel);
+ assert(response?.data?.status === 'Success', 'The operation should succeed');
+ assert(response?.data?.id, 'The ID should not be null');
+
+ const pricingModelId = response?.data?.id;
+ response = await this.adminUserService.pricingApi.readPricingModel(pricingModelId);
+ assert(response?.data?.id === pricingModelId, 'The ID should be: ' + pricingModelId);
+ const parkingPrice: PricingDimension = {
+ price: 0.75,
+ active: true
+ };
+ const fastChargerRestrictions: PricingRestriction = {
+ minPowerkW: 40000
+ };
+ const price4TheEnergy: PricingDimension = {
+ price: 0.35,
+ active: true
+ };
+ const tariff1: PricingDefinition = {
+ name: 'RED Tariff',
+ description: 'Tariff for fast chargers',
+ restrictions: fastChargerRestrictions,
+ dimensions: {
+ // chargingTime: parkingPrice, // parking time is free while charging
+ energy: price4TheEnergy,
+ parkingTime: parkingPrice, // Parking time is not free when not charging
+ }
+ };
+ const lowConsumptionRestrictions: PricingRestriction = {
+ maxPowerkW: 40000,
+ };
+ const tariff2: PricingDefinition = {
+ name: 'GREEN Tariff',
+ description: 'Tariff for low EVSE',
+ restrictions: lowConsumptionRestrictions,
+ dimensions: {
+ chargingTime: parkingPrice,
+ // energy: price4TheEnergy, // do not bill the energy - bill the parking time instead
+ parkingTime: parkingPrice,
+ }
+ };
+ const pricingModel = response?.data;
+ pricingModel.pricingDefinitions = [tariff1, tariff2];
+ response = await this.adminUserService.pricingApi.updatePricingModel(pricingModel);
+ assert(response?.data?.status === 'Success', 'The operation should succeed');
+ }
+
}
const testData: TestData = new TestData();
@@ -401,16 +464,27 @@ describe('Billing Service', function() {
this.pending = !testData.isBillingProperlyConfigured();
this.timeout(1000000);
- describe('with Transaction Billing ON', () => {
+ describe('With component Billing (utbilling)', () => {
before(async () => {
global.database = new MongoDBStorage(config.get('storage'));
await global.database.start();
- testData.tenantContext = await ContextProvider.defaultInstance.getTenantContext(ContextDefinition.TENANT_CONTEXTS.TENANT_BILLING);
- testData.adminUserContext = testData.tenantContext.getUserContext(ContextDefinition.USER_CONTEXTS.DEFAULT_ADMIN);
- testData.adminUserService = new CentralServerService(
- testData.tenantContext.getTenant().subdomain,
- testData.adminUserContext
- );
+ await testData.initialize();
+ });
+
+ describe('Initialization Pricing Model', () => {
+ before(async () => {
+ });
+
+ after(async () => {
+ });
+
+ it('check CRUD operations on a Pricing Model', async () => {
+ await testData.checkPricingModel();
+ });
+ });
+
+ describe('with Transaction Billing ON', () => {
+ before(async () => {
expect(testData.userContext).to.not.be.null;
testData.siteContext = testData.tenantContext.getSiteContext(ContextDefinition.SITE_CONTEXTS.SITE_WITH_OTHER_USER_STOP_AUTHORIZATION);
testData.siteAreaContext = testData.siteContext.getSiteAreaContext(ContextDefinition.SITE_AREA_CONTEXTS.WITH_ACL);
@@ -835,14 +909,6 @@ describe('Billing Service', function() {
describe('with Transaction Billing OFF', () => {
before(async () => {
- global.database = new MongoDBStorage(config.get('storage'));
- await global.database.start();
- testData.tenantContext = await ContextProvider.defaultInstance.getTenantContext(ContextDefinition.TENANT_CONTEXTS.TENANT_BILLING);
- testData.adminUserContext = testData.tenantContext.getUserContext(ContextDefinition.USER_CONTEXTS.DEFAULT_ADMIN);
- testData.adminUserService = new CentralServerService(
- testData.tenantContext.getTenant().subdomain,
- testData.adminUserContext
- );
expect(testData.userContext).to.not.be.null;
testData.siteContext = testData.tenantContext.getSiteContext(ContextDefinition.SITE_CONTEXTS.SITE_WITH_OTHER_USER_STOP_AUTHORIZATION);
testData.siteAreaContext = testData.siteContext.getSiteAreaContext(ContextDefinition.SITE_AREA_CONTEXTS.WITH_ACL);
@@ -879,14 +945,6 @@ describe('Billing Service', function() {
describe('with Transaction Billing + Immediate Billing ON', () => {
before(async () => {
- global.database = new MongoDBStorage(config.get('storage'));
- await global.database.start();
- testData.tenantContext = await ContextProvider.defaultInstance.getTenantContext(ContextDefinition.TENANT_CONTEXTS.TENANT_BILLING);
- testData.adminUserContext = testData.tenantContext.getUserContext(ContextDefinition.USER_CONTEXTS.DEFAULT_ADMIN);
- testData.adminUserService = new CentralServerService(
- testData.tenantContext.getTenant().subdomain,
- testData.adminUserContext
- );
expect(testData.userContext).to.not.be.null;
testData.siteContext = testData.tenantContext.getSiteContext(ContextDefinition.SITE_CONTEXTS.SITE_WITH_OTHER_USER_STOP_AUTHORIZATION);
testData.siteAreaContext = testData.siteContext.getSiteAreaContext(ContextDefinition.SITE_AREA_CONTEXTS.WITH_ACL);
@@ -920,14 +978,6 @@ describe('Billing Service', function() {
describe('with Transaction Billing + Periodic Billing ON', () => {
before(async () => {
- global.database = new MongoDBStorage(config.get('storage'));
- await global.database.start();
- testData.tenantContext = await ContextProvider.defaultInstance.getTenantContext(ContextDefinition.TENANT_CONTEXTS.TENANT_BILLING);
- testData.adminUserContext = testData.tenantContext.getUserContext(ContextDefinition.USER_CONTEXTS.DEFAULT_ADMIN);
- testData.adminUserService = new CentralServerService(
- testData.tenantContext.getTenant().subdomain,
- testData.adminUserContext
- );
expect(testData.userContext).to.not.be.null;
testData.siteContext = testData.tenantContext.getSiteContext(ContextDefinition.SITE_CONTEXTS.SITE_WITH_OTHER_USER_STOP_AUTHORIZATION);
testData.siteAreaContext = testData.siteContext.getSiteAreaContext(ContextDefinition.SITE_AREA_CONTEXTS.WITH_ACL);
@@ -967,5 +1017,6 @@ describe('Billing Service', function() {
});
});
+ });
});

billing/princing - some test refactoring/cleanup