pricing - better logs with less data
MODIFY
src/integration/billing/stripe/StripeBillingIntegration.ts
src/integration/billing/stripe/StripeBillingIntegration.ts
@@ -1070,16 +1070,16 @@ export default class StripeBillingIntegration extends BillingIntegration {
private convertToBillingInvoiceItem(transaction: Transaction) : BillingInvoiceItem {
if (FeatureToggles.isFeatureActive(Feature.PRICING_NEW_MODEL) && transaction.pricingModel) {
// Built-in Pricing
- return this._convertPricingDataToBillingInvoiceItem(transaction);
+ return this.convertPricingDataToBillingInvoiceItem(transaction);
}
// Simple Pricing - Do it the old way!
return this._convertToBillingInvoiceItem(transaction);
}
- private _convertPricingDataToBillingInvoiceItem(transaction: Transaction) : BillingInvoiceItem {
+ private convertPricingDataToBillingInvoiceItem(transaction: Transaction) : BillingInvoiceItem {
const transactionID = transaction.id;
const currency = transaction.stop.priceUnit;
- const pricingData: PricedConsumptionData[] = this._extractTransactionPricingData(transaction);
+ const pricingData: PricedConsumptionData[] = this.extractTransactionPricingData(transaction);
const billingInvoiceItem: BillingInvoiceItem = {
transactionID,
currency,
@@ -1100,18 +1100,22 @@ export default class StripeBillingIntegration extends BillingIntegration {
return billingInvoiceItem ;
}
- private _extractTransactionPricingData(transaction: Transaction) : PricedConsumptionData[] {
+ private extractTransactionPricingData(transaction: Transaction) : PricedConsumptionData[] {
const pricingModel = Object.freeze(transaction.pricingModel);
let pricingData: PricedConsumptionData[] = PricingEngine.extractFinalPricingData(pricingModel);
if (!FeatureToggles.isFeatureActive(Feature.BILLING_SHOW_PRICING_DETAIL)) {
// Accumulate data per dimensions = less details, less disputes
pricingData = [ PricingHelper.accumulatePricedConsumption(pricingData) ] ;
}
- pricingData = pricingData.map((pricingConsumptionData) => this._enrichTransactionPricingData(transaction, pricingConsumptionData));
+ pricingData = pricingData.map((pricingConsumptionData) => this.enrichTransactionPricingData(transaction, pricingConsumptionData));
+ void PricingHelper.logInfo(this.tenant, transaction, {
+ message: `Final pricing data extraction for transaction: ${transaction.id}`,
+ detailedMessages: pricingData,
+ });
return pricingData;
}
- private _enrichTransactionPricingData(transaction: Transaction, pricingConsumptionData: PricedConsumptionData) : PricedConsumptionData {
+ private enrichTransactionPricingData(transaction: Transaction, pricingConsumptionData: PricedConsumptionData) : PricedConsumptionData {
// -------------------------------------------------------------------------------
// TODO - so far we use the same tax rates for all invoice items!
// -------------------------------------------------------------------------------

MODIFY
src/integration/pricing/ConsumptionPricer.ts
src/integration/pricing/ConsumptionPricer.ts
-import PricingDefinition, { DimensionType, PricedConsumptionData, PricedDimensionData, PricingDimension, PricingRestriction, ResolvedPricingModel } from '../../types/Pricing';
+import { DimensionType, PricedConsumptionData, PricedDimensionData, PricingDimension, PricingRestriction, ResolvedPricingDefinition, ResolvedPricingModel } from '../../types/Pricing';
import Consumption from '../../types/Consumption';
import Tenant from '../../types/Tenant';
@@ -10,7 +10,7 @@ export default class ConsumptionPricer {
tenant: Tenant;
pricingModel: ResolvedPricingModel;
consumptionData: Consumption;
- actualPricingDefinitions: PricingDefinition[];
+ actualPricingDefinitions: ResolvedPricingDefinition[];
constructor(tenant: Tenant, pricingModel: ResolvedPricingModel, consumptionData: Consumption) {
this.tenant = tenant;
@@ -39,7 +39,7 @@ export default class ConsumptionPricer {
return pricingConsumptionData;
}
- private checkPricingDefinitionRestrictions(pricingDefinition: PricingDefinition) : PricingDefinition {
+ private checkPricingDefinitionRestrictions(pricingDefinition: ResolvedPricingDefinition) : ResolvedPricingDefinition {
if (pricingDefinition.restrictions) {
if (!this.checkMinEnergy(pricingDefinition.restrictions)
|| !this.checkMaxEnergy(pricingDefinition.restrictions)
@@ -142,7 +142,7 @@ export default class ConsumptionPricer {
this.absorbConsumption();
}
- private enrichPricedData(pricedData: PricedDimensionData, activePricingDefinition: PricingDefinition) : PricedDimensionData {
+ private enrichPricedData(pricedData: PricedDimensionData, activePricingDefinition: ResolvedPricingDefinition) : PricedDimensionData {
if (pricedData) {
pricedData.sourceName = activePricingDefinition.name;
}
@@ -225,14 +225,14 @@ export default class ConsumptionPricer {
this.pricingModel.pricerContext.lastAbsorbedParkingTime = this.consumptionData.endedAt;
}
- private getActiveDefinition4Dimension(actualPricingDefinitions: PricingDefinition[], dimensionType: string): PricingDefinition {
+ private getActiveDefinition4Dimension(actualPricingDefinitions: ResolvedPricingDefinition[], dimensionType: string): ResolvedPricingDefinition {
// Search for the first pricing definition matching the current dimension type
return actualPricingDefinitions.find((pricingDefinition) =>
this.checkPricingDimensionRestrictions(pricingDefinition, dimensionType)
);
}
- private checkPricingDimensionRestrictions(pricingDefinition: PricingDefinition, dimensionType: string) : PricingDefinition {
+ private checkPricingDimensionRestrictions(pricingDefinition: ResolvedPricingDefinition, dimensionType: string) : ResolvedPricingDefinition {
const pricingDimension: PricingDimension = pricingDefinition.dimensions[dimensionType];
if (pricingDimension?.active) {
return pricingDefinition;

MODIFY
src/integration/pricing/PricingEngine.ts
src/integration/pricing/PricingEngine.ts
+/* eslint-disable max-len */
import FeatureToggles, { Feature } from '../../utils/FeatureToggles';
-import PricingDefinition, { PricedConsumptionData, PricingStaticRestriction, ResolvedPricingModel } from '../../types/Pricing';
+import PricingDefinition, { PricedConsumptionData, PricingStaticRestriction, ResolvedPricingDefinition, ResolvedPricingModel } from '../../types/Pricing';
import ChargingStation from '../../types/ChargingStation';
import Constants from '../../utils/Constants';
@@ -16,7 +17,7 @@ export default class PricingEngine {
public static async resolvePricingContext(tenant: Tenant, transaction: Transaction, chargingStation: ChargingStation): Promise<ResolvedPricingModel> {
// Merge the pricing definitions from the different contexts
- const pricingDefinitions: PricingDefinition[] = [];
+ const pricingDefinitions: ResolvedPricingDefinition[] = [];
if (FeatureToggles.isFeatureActive(Feature.PRICING_CHECK_BACKWARD_COMPATIBILITY)) {
// Do nothing - this should trigger a fallback to the simple pricing logic
} else {
@@ -55,11 +56,13 @@ export default class PricingEngine {
return pricedData.filter((pricingConsumptionData) => !!pricingConsumptionData);
}
- private static async getPricingDefinitions4Entity(tenant: Tenant, transaction: Transaction, chargingStation: ChargingStation, entityID: string): Promise<PricingDefinition[]> {
+ private static async getPricingDefinitions4Entity(tenant: Tenant, transaction: Transaction, chargingStation: ChargingStation, entityID: string): Promise<ResolvedPricingDefinition[]> {
let pricingDefinitions = await PricingEngine._getPricingDefinitions4Entity(tenant, entityID);
pricingDefinitions = pricingDefinitions || [];
const actualPricingDefinitions = pricingDefinitions.filter((pricingDefinition) =>
PricingEngine.checkStaticRestrictions(pricingDefinition, transaction, chargingStation)
+ ).map((pricingDefinition) =>
+ PricingEngine.shrinkPricingDefinition(pricingDefinition)
);
await PricingHelper.logInfo(tenant, transaction, {
method: 'getPricingDefinitions4Entity',
@@ -95,6 +98,20 @@ export default class PricingEngine {
return pricingDefinition;
}
+ private static shrinkPricingDefinition(pricingDefinition: PricingDefinition): ResolvedPricingDefinition {
+ const resolvedPricingDefinition: ResolvedPricingDefinition = {
+ id: pricingDefinition.id,
+ entityID: pricingDefinition.entityID,
+ entityType: pricingDefinition.entityType,
+ name: pricingDefinition.name,
+ description: pricingDefinition.name,
+ staticRestrictions: pricingDefinition.staticRestrictions,
+ restrictions: pricingDefinition.restrictions,
+ dimensions: pricingDefinition.dimensions,
+ };
+ return resolvedPricingDefinition;
+ }
+
private static checkDateValidity(staticRestrictions: PricingStaticRestriction, transaction: Transaction): boolean {
if (!Utils.isNullOrUndefined(staticRestrictions.validFrom)) {
if (moment(transaction.timestamp).isBefore(staticRestrictions.validFrom)) {
@@ -129,7 +146,7 @@ export default class PricingEngine {
return true;
}
- private static extractFinalPricedConsumptionData(pricingDefinition: PricingDefinition): PricedConsumptionData {
+ private static extractFinalPricedConsumptionData(pricingDefinition: ResolvedPricingDefinition): PricedConsumptionData {
const flatFee = pricingDefinition.dimensions.flatFee?.pricedData;
const energy = pricingDefinition.dimensions.energy?.pricedData;
const chargingTime = pricingDefinition.dimensions.chargingTime?.pricedData;

MODIFY
src/integration/pricing/simple-pricing/BuiltInPricingIntegration.ts
src/integration/pricing/simple-pricing/BuiltInPricingIntegration.ts
/* eslint-disable max-len */
import FeatureToggles, { Feature } from '../../../utils/FeatureToggles';
-import PricingDefinition, { PricedConsumption, PricingDimensions, PricingSource, ResolvedPricingModel } from '../../../types/Pricing';
+import { PricedConsumption, PricingDimensions, PricingSource, ResolvedPricingDefinition, ResolvedPricingModel } from '../../../types/Pricing';
import ChargingStation from '../../../types/ChargingStation';
import Consumption from '../../../types/Consumption';
@@ -37,6 +37,7 @@ export default class BuiltInPricingIntegration extends PricingIntegration<Simple
const pricedConsumption = await this.computePrice(transaction, consumptionData, chargingStation);
await PricingHelper.logInfo(this.tenant, transaction, {
message: `Session STOP - Transaction: ${transaction.id} - Accumulated amount: ${pricedConsumption.cumulatedRoundedAmount} ${pricedConsumption.currencyCode}`,
+ detailedMessages: { pricedConsumption }
});
return pricedConsumption;
}
@@ -95,9 +96,9 @@ export default class BuiltInPricingIntegration extends PricingIntegration<Simple
return resolvedPricingModel;
}
- private getDefaultPricingDefinition(): PricingDefinition {
+ private getDefaultPricingDefinition(): ResolvedPricingDefinition {
// Defaults to the simple pricing settings
- return {
+ const simplePricingDefinition: ResolvedPricingDefinition = {
// TODO - translate default tariff name
name: 'Default Tariff',
description: 'Tariff based on simple pricing settings',
@@ -107,7 +108,8 @@ export default class BuiltInPricingIntegration extends PricingIntegration<Simple
price: this.setting.price,
}
}
- } as PricingDefinition;
+ };
+ return simplePricingDefinition;
}
}

MODIFY
src/types/Pricing.ts
src/types/Pricing.ts
@@ -21,7 +21,7 @@ export enum DimensionType {
export interface ResolvedPricingModel {
// Put there only the information that is to be kept with the Transaction
- pricingDefinitions: PricingDefinition[];
+ pricingDefinitions: ResolvedPricingDefinition[];
pricerContext: ConsumptionPricerContext;
}
@@ -33,10 +33,10 @@ export interface ConsumptionPricerContext {
lastAbsorbedParkingTime?: Date, // IMPORTANT - used to price PT when stepSize is set!
}
-export default interface PricingDefinition extends CreatedUpdatedProps, AuthorizationActions {
- id: string;
- entityID: string; // id of the entity the pricing definition belongs to
- entityType: PricingEntity; // Type of the entity this model belongs to
+interface PricingDefinitionInternal {
+ id?: string;
+ entityID?: string; // id of the entity the pricing definition belongs to
+ entityType?: PricingEntity; // Type of the entity this model belongs to
name: string; // Short marketing name - e.g.: BLUE Tariff,
description: string; // A long description to explain it, e.g.: Time-based pricing for low charging stations
staticRestrictions?: PricingStaticRestriction;
@@ -44,6 +44,16 @@ export default interface PricingDefinition extends CreatedUpdatedProps, Authoriz
dimensions: PricingDimensions;
}
+// eslint-disable-next-line @typescript-eslint/no-empty-interface
+export interface ResolvedPricingDefinition extends PricingDefinitionInternal {
+}
+
+export default interface PricingDefinition extends PricingDefinitionInternal, CreatedUpdatedProps, AuthorizationActions {
+ id: string;
+ entityID: string;
+ entityType: PricingEntity;
+}
+
export interface PricingDimensions {
flatFee?: PricingDimension; // Flat fee, no unit
energy?: PricingDimension; // Defined in kWh, step_size multiplier: 1 Wh

pricing - better logs with less data