pricing - PricedDimensionData
MODIFY
src/integration/billing/stripe/StripeBillingIntegration.ts
src/integration/billing/stripe/StripeBillingIntegration.ts
import { AsyncTaskType, AsyncTasks } from '../../../types/AsyncTask';
/* eslint-disable @typescript-eslint/member-ordering */
import { BillingDataTransactionStart, BillingDataTransactionStop, BillingDataTransactionUpdate, BillingInvoice, BillingInvoiceItem, BillingInvoiceStatus, BillingOperationResult, BillingPaymentMethod, BillingStatus, BillingTax, BillingUser, BillingUserData } from '../../../types/Billing';
-import { DimensionType, PricingConsumptionData, PricingDimensionData } from '../../../types/Pricing';
+import { DimensionType, PricedConsumptionData, PricedDimensionData } from '../../../types/Pricing';
import FeatureToggles, { Feature } from '../../../utils/FeatureToggles';
import StripeHelpers, { StripeChargeOperationResult } from './StripeHelpers';
import Transaction, { StartTransactionErrorCode } from '../../../types/Transaction';
@@ -871,7 +871,7 @@ export default class StripeBillingIntegration extends BillingIntegration {
}
private async _createStripeInvoiceItems4PricingConsumptionData(customerID: string,
- billingInvoiceItem: BillingInvoiceItem, pricedData: PricingConsumptionData, invoiceID?: string): Promise<void> {
+ billingInvoiceItem: BillingInvoiceItem, pricedData: PricedConsumptionData, invoiceID?: string): Promise<void> {
/* --------------------------------------------------------------------------------
Convert pricing information to STRIPE expected data
-----------------------------------------------------------------------------------
@@ -891,9 +891,9 @@ export default class StripeBillingIntegration extends BillingIntegration {
}
private async _createStripeInvoiceItem4Dimension(customerID: string, dimension: string,
- billingInvoiceItem: BillingInvoiceItem, pricedData: PricingConsumptionData, invoiceID?: string): Promise<Stripe.InvoiceItemCreateParams> {
+ billingInvoiceItem: BillingInvoiceItem, pricedData: PricedConsumptionData, invoiceID?: string): Promise<Stripe.InvoiceItemCreateParams> {
// data for the current dimension (energy | parkingTime, etc)
- const dimensionData: PricingDimensionData = pricedData[dimension];
+ const dimensionData: PricedDimensionData = pricedData[dimension];
if (!dimensionData || !dimensionData.amount || !dimensionData.quantity) {
// Do not bill that dimension
return null;
@@ -1059,7 +1059,7 @@ export default class StripeBillingIntegration extends BillingIntegration {
private _convertPricingDataToBillingInvoiceItem(transaction: Transaction) : BillingInvoiceItem {
const transactionID = transaction.id;
const currency = transaction.stop.priceUnit;
- const pricingData: PricingConsumptionData[] = this._extractTransactionPricingData(transaction);
+ const pricingData: PricedConsumptionData[] = this._extractTransactionPricingData(transaction);
const billingInvoiceItem: BillingInvoiceItem = {
transactionID,
currency,
@@ -1076,14 +1076,14 @@ export default class StripeBillingIntegration extends BillingIntegration {
return billingInvoiceItem ;
}
- private _extractTransactionPricingData(transaction: Transaction) : PricingConsumptionData[] {
+ private _extractTransactionPricingData(transaction: Transaction) : PricedConsumptionData[] {
const pricingModel = Object.freeze(transaction.pricingModel);
- let pricingData: PricingConsumptionData[] = PricingEngine.extractFinalPricingData(pricingModel);
+ let pricingData: PricedConsumptionData[] = PricingEngine.extractFinalPricingData(pricingModel);
pricingData = pricingData.map((pricingConsumptionData) => this._enrichTransactionPricingData(transaction, pricingConsumptionData));
return pricingData;
}
- private _enrichTransactionPricingData(transaction: Transaction, pricingConsumptionData: PricingConsumptionData) : PricingConsumptionData {
+ private _enrichTransactionPricingData(transaction: Transaction, pricingConsumptionData: PricedConsumptionData) : PricedConsumptionData {
// -------------------------------------------------------------------------------
// TODO - so far we use the same tax rates for all invoice items!
// -------------------------------------------------------------------------------

MODIFY
src/integration/pricing/PricingEngine.ts
src/integration/pricing/PricingEngine.ts
/* eslint-disable @typescript-eslint/member-ordering */
-import PricingModel, { DimensionType, PricingConsumptionData, PricingDefinition, PricingDimension, PricingDimensionData, PricingRestriction, ResolvedPricingModel } from '../../types/Pricing';
+import PricingModel, { DimensionType, PricedConsumptionData, PricedDimensionData, PricingDefinition, PricingDimension, PricingRestriction, ResolvedPricingModel } from '../../types/Pricing';
import Consumption from '../../types/Consumption';
import PricingStorage from '../../storage/mongodb/PricingStorage';
@@ -139,7 +139,7 @@ export default class PricingEngine {
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
- private static priceFlatFeeConsumption(pricingDefinitions: PricingDefinition[], consumptionData: Consumption): PricingDimensionData {
+ private static priceFlatFeeConsumption(pricingDefinitions: PricingDefinition[], consumptionData: Consumption): PricedDimensionData {
const activePricingDefinition = PricingEngine.getActiveDefinition4Dimension(pricingDefinitions, DimensionType.FLAT_FEE);
if (activePricingDefinition) {
const dimensionToPrice = activePricingDefinition.dimensions.flatFee;
@@ -151,7 +151,7 @@ export default class PricingEngine {
}
}
- private static priceEnergyConsumption(pricingDefinitions: PricingDefinition[], consumptionData: Consumption): PricingDimensionData {
+ private static priceEnergyConsumption(pricingDefinitions: PricingDefinition[], consumptionData: Consumption): PricedDimensionData {
const activePricingDefinition = PricingEngine.getActiveDefinition4Dimension(pricingDefinitions, DimensionType.ENERGY);
if (activePricingDefinition) {
const dimensionToPrice = activePricingDefinition.dimensions.energy;
@@ -163,7 +163,7 @@ export default class PricingEngine {
}
}
- private static priceParkingTimeConsumption(pricingDefinitions: PricingDefinition[], consumptionData: Consumption): PricingDimensionData {
+ private static priceParkingTimeConsumption(pricingDefinitions: PricingDefinition[], consumptionData: Consumption): PricedDimensionData {
const activePricingDefinition = PricingEngine.getActiveDefinition4Dimension(pricingDefinitions, DimensionType.PARKING_TIME);
if (activePricingDefinition) {
const dimensionToPrice = activePricingDefinition.dimensions.parkingTime;
@@ -175,7 +175,7 @@ export default class PricingEngine {
}
}
- private static priceChargingTimeConsumption(pricingDefinitions: PricingDefinition[], consumptionData: Consumption): PricingDimensionData {
+ private static priceChargingTimeConsumption(pricingDefinitions: PricingDefinition[], consumptionData: Consumption): PricedDimensionData {
const activePricingDefinition = PricingEngine.getActiveDefinition4Dimension(pricingDefinitions, DimensionType.CHARGING_TIME);
if (activePricingDefinition) {
const dimensionToPrice = activePricingDefinition.dimensions.chargingTime;
@@ -211,7 +211,7 @@ export default class PricingEngine {
return null;
}
- static priceFlatFeeDimension(pricingDimension: PricingDimension): PricingDimensionData {
+ static priceFlatFeeDimension(pricingDimension: PricingDimension): PricedDimensionData {
const unitPrice = pricingDimension.price || 0;
if (pricingDimension.pricedData) {
// This should not happen for the flatFee dimension - Flat Fee is billed only once per session
@@ -233,7 +233,7 @@ export default class PricingEngine {
return pricingDimension.pricedData;
}
- static priceEnergyDimension(pricingDimension: PricingDimension, cumulatedConsumptionWh: number): PricingDimensionData {
+ static priceEnergyDimension(pricingDimension: PricingDimension, cumulatedConsumptionWh: number): PricedDimensionData {
let amount: number;
let consumptionkWh: number;
const unitPrice = pricingDimension.price || 0;
@@ -251,7 +251,7 @@ export default class PricingEngine {
if (previousData) {
// The new priced data is the delta
const delta = Utils.createDecimal(amount).minus(previousData?.amount || 0).toNumber();
- const newData : PricingDimensionData = {
+ const newData : PricedDimensionData = {
unitPrice: unitPrice,
amount: delta,
roundedAmount: Utils.truncTo(delta, 2),
@@ -274,7 +274,7 @@ export default class PricingEngine {
return pricingDimension.pricedData;
}
- static priceTimeBasedDimension(pricingDimension: PricingDimension, seconds: number): PricingDimensionData {
+ static priceTimeBasedDimension(pricingDimension: PricingDimension, seconds: number): PricedDimensionData {
let amount: number;
let hours: number;
const unitPrice = pricingDimension.price || 0;
@@ -292,7 +292,7 @@ export default class PricingEngine {
if (previousData) {
// The new priced data is the delta
const delta = Utils.createDecimal(amount).minus(previousData?.amount || 0).toNumber();
- const newData : PricingDimensionData = {
+ const newData : PricedDimensionData = {
unitPrice: unitPrice,
roundedAmount: Utils.truncTo(delta, 2),
amount: delta,
@@ -315,7 +315,7 @@ export default class PricingEngine {
return pricingDimension.pricedData;
}
- static priceConsumption(tenant: Tenant, pricingModel: ResolvedPricingModel, consumptionData: Consumption): PricingConsumptionData {
+ static priceConsumption(tenant: Tenant, pricingModel: ResolvedPricingModel, consumptionData: Consumption): PricedConsumptionData {
// Check the restrictions to find the pricing definition matching the current context
let actualPricingDefinitions = pricingModel.pricingDefinitions.filter((pricingDefinition) =>
PricingEngine.checkPricingDefinitionRestrictions(pricingDefinition, consumptionData)
@@ -326,19 +326,19 @@ export default class PricingEngine {
// TODO - to be clarified! - Shall we mix several pricing definitions for a single transaction?
actualPricingDefinitions = [ actualPricingDefinitions?.[0] ];
}
- let flatFee: PricingDimensionData = null;
+ let flatFee: PricedDimensionData = null;
if (!pricingModel.flatFeeAlreadyPriced) {
// Flat fee must not be priced only once
flatFee = PricingEngine.priceFlatFeeConsumption(actualPricingDefinitions, consumptionData);
pricingModel.flatFeeAlreadyPriced = !!flatFee;
}
// Build the consumption data for each dimension
- const energy: PricingDimensionData = PricingEngine.priceEnergyConsumption(actualPricingDefinitions, consumptionData);
- const chargingTime: PricingDimensionData = PricingEngine.priceChargingTimeConsumption(actualPricingDefinitions, consumptionData);
- const parkingTime: PricingDimensionData = PricingEngine.priceParkingTimeConsumption(actualPricingDefinitions, consumptionData);
+ const energy: PricedDimensionData = PricingEngine.priceEnergyConsumption(actualPricingDefinitions, consumptionData);
+ const chargingTime: PricedDimensionData = PricingEngine.priceChargingTimeConsumption(actualPricingDefinitions, consumptionData);
+ const parkingTime: PricedDimensionData = PricingEngine.priceParkingTimeConsumption(actualPricingDefinitions, consumptionData);
// Return all dimensions
- const pricingConsumptionData: PricingConsumptionData = {
+ const pricingConsumptionData: PricedConsumptionData = {
flatFee,
energy,
chargingTime,
@@ -347,16 +347,16 @@ export default class PricingEngine {
return pricingConsumptionData;
}
- static extractFinalPricingData(pricingModel: ResolvedPricingModel): PricingConsumptionData[] {
+ static extractFinalPricingData(pricingModel: ResolvedPricingModel): PricedConsumptionData[] {
// Iterate throw the list of pricing definitions
- const pricedData: PricingConsumptionData[] = pricingModel.pricingDefinitions.map((pricingDefinition) =>
+ const pricedData: PricedConsumptionData[] = pricingModel.pricingDefinitions.map((pricingDefinition) =>
PricingEngine.extractFinalPricedConsumptionData(pricingDefinition)
);
// Remove null/undefined entries (if any)
return pricedData.filter((pricingConsumptionData) => !!pricingConsumptionData);
}
- static extractFinalPricedConsumptionData(pricingDefinition: PricingDefinition): PricingConsumptionData {
+ static extractFinalPricedConsumptionData(pricingDefinition: PricingDefinition): PricedConsumptionData {
const flatFee = pricingDefinition.dimensions.flatFee?.pricedData;
const energy = pricingDefinition.dimensions.energy?.pricedData;
const chargingTime = pricingDefinition.dimensions.chargingTime?.pricedData;
@@ -373,7 +373,7 @@ export default class PricingEngine {
return null;
}
- static extractPricedDataFromDimension(pricedData: PricingDimensionData[], pricingDefinition: PricingDefinition, dimensionType: string): void {
+ static extractPricedDataFromDimension(pricedData: PricedDimensionData[], pricingDefinition: PricingDefinition, dimensionType: string): void {
const pricingDimensionData = pricingDefinition.dimensions[dimensionType]?.pricedData;
if (pricingDimensionData) {
pricedData.push(pricingDimensionData);

MODIFY
src/types/Billing.ts
src/types/Billing.ts
import { ActionsResponse } from './GlobalType';
-import { PricingConsumptionData } from './Pricing';
+import { PricedConsumptionData } from './Pricing';
import User from './User';
export interface TransactionBillingData {
@@ -84,7 +84,7 @@ export interface BillingInvoice {
export interface BillingInvoiceItem {
transactionID: number;
currency: string;
- pricingData: PricingConsumptionData[]
+ pricingData: PricedConsumptionData[]
metadata?: {
// Just a flat list of key/value pairs!
[name: string]: string | number | null;
@@ -93,7 +93,7 @@ export interface BillingInvoiceItem {
export interface BillingSessionData {
transactionID: number;
- pricingData: PricingConsumptionData[];
+ pricingData: PricedConsumptionData[];
}
export enum BillingInvoiceStatus {

MODIFY
src/types/Pricing.ts
src/types/Pricing.ts
@@ -38,7 +38,7 @@ 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
stepSize?: number, // Minimum amount to be billed. This unit will be billed in this step_size blocks. For example: if type is time and step_size is 300, then time will be billed in blocks of 5 minutes, so if 6 minutes is used, 10 minutes (2 blocks of step_size) will be billed.
- pricedData?: PricingDimensionData // Information set dynamically while charging
+ pricedData?: PricedDimensionData // Information set dynamically while charging
}
export interface PricingRestriction {
@@ -73,7 +73,7 @@ export interface PricedConsumption {
currencyCode: string;
pricingSource: PricingSource;
pricingModel?: ResolvedPricingModel
- pricingConsumptionData?: PricingConsumptionData
+ pricingConsumptionData?: PricedConsumptionData
}
export enum PricingSource {
@@ -82,14 +82,14 @@ export enum PricingSource {
OCPI = 'ocpi',
}
-export interface PricingConsumptionData {
- flatFee?: PricingDimensionData,
- energy?: PricingDimensionData,
- parkingTime?: PricingDimensionData,
- chargingTime?: PricingDimensionData,
+export interface PricedConsumptionData {
+ flatFee?: PricedDimensionData,
+ energy?: PricedDimensionData,
+ parkingTime?: PricedDimensionData,
+ chargingTime?: PricedDimensionData,
}
-export interface PricingDimensionData {
+export interface PricedDimensionData {
unitPrice: number,
amount: number;
roundedAmount: number;

MODIFY
src/types/Transaction.ts
src/types/Transaction.ts
import { Car, CarCatalog } from './Car';
import { ChargePointStatus, OCPP15TransactionData, OCPPMeterValue } from './ocpp/OCPPServer';
import Consumption, { AbstractCurrentConsumption } from './Consumption';
-import PricingModel, { PricingConsumptionData, ResolvedPricingModel } from './Pricing';
+import PricingModel, { PricedConsumptionData, ResolvedPricingModel } from './Pricing';
import ChargingStation from '../types/ChargingStation';
import Company from './Company';

MODIFY
test/api/BillingStripeTestData.ts
test/api/BillingStripeTestData.ts
import { BillingInvoice, BillingInvoiceItem, BillingInvoiceStatus, BillingOperationResult, BillingUser, BillingUserData } from '../../src/types/Billing';
import { BillingSettings, BillingSettingsType, SettingDB } from '../../src/types/Setting';
import FeatureToggles, { Feature } from '../../src/utils/FeatureToggles';
-import PricingModel, { PricingConsumptionData, PricingDefinition, PricingDimension, PricingRestriction } from '../../src/types/Pricing';
+import PricingModel, { PricedConsumptionData, PricingDefinition, PricingDimension, PricingRestriction } from '../../src/types/Pricing';
import chai, { assert, expect } from 'chai';
import BillingStorage from '../../src/storage/mongodb/BillingStorage';
@@ -277,7 +277,7 @@ export default class StripeIntegrationTestData {
// array of tax ids to apply to the line item
const taxes = (consumptionTestData.taxId) ? [ consumptionTestData.taxId ] : [];
// Pricing/Consumption Data
- const pricingConsumptionData: PricingConsumptionData = {
+ const pricingConsumptionData: PricedConsumptionData = {
energy: {
itemDescription: `Energy consumption - ${consumptionTestData.energyConsumptionkWh} kWh * ${consumptionTestData.energyAmount / consumptionTestData.energyConsumptionkWh} Eur`,
unitPrice: Utils.createDecimal(consumptionTestData.energyAmount).div(consumptionTestData.energyConsumptionkWh).toNumber(),

pricing - PricedDimensionData