billing - afirev - threadhold
MODIFY
src/integration/billing/BillingIntegration.ts
src/integration/billing/BillingIntegration.ts
@@ -9,6 +9,7 @@ import Constants from '../../utils/Constants';
import { DataResult } from '../../types/DataResult';
import { Decimal } from 'decimal.js';
import Logging from '../../utils/Logging';
+import LoggingHelper from '../../utils/LoggingHelper';
import NotificationHandler from '../../notification/NotificationHandler';
import { Promise } from 'bluebird';
import { Request } from 'express';
@@ -249,16 +250,52 @@ export default abstract class BillingIntegration {
}
}
- private async _getUsersWithNoBillingData(): Promise<User[]> {
- const newUsers = await UserStorage.getUsers(this.tenant,
- {
- statuses: [UserStatus.ACTIVE],
- notSynchronizedBillingData: true
- }, Constants.DB_PARAMS_MAX_LIMIT);
- if (newUsers.count > 0) {
- return newUsers.result;
+ protected async checkBillingDataThreshold(transaction: Transaction): Promise<boolean> {
+ // Do not bill suspicious StopTransaction events
+ if (!Utils.isDevelopmentEnv()) {
+ const timeSpent = this.computeTimeSpentInSeconds(transaction);
+ if (timeSpent < Constants.AFIREV_MINIMAL_DURATION_THRESHOLD /* 2 minutes */) {
+ await Logging.logWarning({
+ ...LoggingHelper.getTransactionProperties(transaction),
+ tenantID: this.tenant.id,
+ user: transaction.userID,
+ action: ServerAction.BILLING_TRANSACTION,
+ module: MODULE_NAME, method: 'stopTransaction',
+ message: `Transaction duration is too short - billing operation has been aborted - transaction ID: ${transaction.id}`,
+ });
+ // Abort the billing process - thresholds are not met!
+ return false;
}
- return [];
+ if (transaction.stop.totalConsumptionWh < Constants.AFIREV_MINIMAL_CONSUMPTION_THRESHOLD /* 0.5 kW.h */) {
+ await Logging.logWarning({
+ ...LoggingHelper.getTransactionProperties(transaction),
+ tenantID: this.tenant.id,
+ user: transaction.userID,
+ action: ServerAction.BILLING_TRANSACTION,
+ module: MODULE_NAME, method: 'stopTransaction',
+ message: `Transaction consumption is too low - billing operation has been aborted - transaction ID: ${transaction.id}`,
+ });
+ // Abort the billing process - thresholds are not met!
+ return false;
+ }
+ }
+ // Session data seem to be consistent
+ return true;
+ }
+
+ protected convertTimeSpentToString(transaction: Transaction): string {
+ const totalDuration = this.computeTimeSpentInSeconds(transaction);
+ return moment.duration(totalDuration, 's').format('h[h]mm', { trim: false });
+ }
+
+ protected computeTimeSpentInSeconds(transaction: Transaction): number {
+ let totalDuration: number;
+ if (!transaction.stop) {
+ totalDuration = moment.duration(moment(transaction.lastConsumption.timestamp).diff(moment(transaction.timestamp))).asSeconds();
+ } else {
+ totalDuration = moment.duration(moment(transaction.stop.timestamp).diff(moment(transaction.timestamp))).asSeconds();
+ }
+ return totalDuration;
}
private async _synchronizeUser(user: User, forceMode = false): Promise<BillingUser> {

MODIFY
src/integration/billing/stripe/StripeBillingIntegration.ts
src/integration/billing/stripe/StripeBillingIntegration.ts
@@ -1033,29 +1033,6 @@ export default class StripeBillingIntegration extends BillingIntegration {
};
}
- private async checkBillingDataThreshold(transaction: Transaction): Promise<boolean> {
- // Do not bill suspicious StopTransaction events
- if (!Utils.isDevelopmentEnv()) {
- // Suspicious StopTransaction may occur after a 'Housing temperature approaching limit' error on some charging stations
- const timeSpent = this.computeTimeSpentInSeconds(transaction);
- // TODO - make it part of the pricing or billing settings!
- if (timeSpent < 60 /* seconds */ || transaction.stop.totalConsumptionWh < 1000 /* 1kWh */) {
- await Logging.logWarning({
- ...LoggingHelper.getTransactionProperties(transaction),
- tenantID: this.tenant.id,
- user: transaction.userID,
- action: ServerAction.BILLING_TRANSACTION,
- module: MODULE_NAME, method: 'stopTransaction',
- message: `Transaction data is suspicious - billing operation has been aborted - transaction ID: ${transaction.id}`,
- });
- // Abort the billing process - thresholds are not met!
- return false;
- }
- }
- // billing data sounds correct
- return true;
- }
-
public async billTransaction(transaction: Transaction): Promise<BillingDataTransactionStop> {
// Check Stripe
await this.checkConnection();
@@ -1359,21 +1336,6 @@ export default class StripeBillingIntegration extends BillingIntegration {
return transaction.user.locale ? transaction.user.locale.replace('_', '-') : Constants.DEFAULT_LOCALE.replace('_', '-');
}
- private computeTimeSpentInSeconds(transaction: Transaction): number {
- let totalDuration: number;
- if (!transaction.stop) {
- totalDuration = moment.duration(moment(transaction.lastConsumption.timestamp).diff(moment(transaction.timestamp))).asSeconds();
- } else {
- totalDuration = moment.duration(moment(transaction.stop.timestamp).diff(moment(transaction.timestamp))).asSeconds();
- }
- return totalDuration;
- }
-
- private convertTimeSpentToString(transaction: Transaction): string {
- const totalDuration = this.computeTimeSpentInSeconds(transaction);
- return moment.duration(totalDuration, 's').format('h[h]mm', { trim: false });
- }
-
// eslint-disable-next-line @typescript-eslint/require-await, @typescript-eslint/no-unused-vars
public async checkIfUserCanBeCreated(user: User): Promise<boolean> {
// throw new BackendError({

MODIFY
src/storage/mongodb/TransactionStorage.ts
src/storage/mongodb/TransactionStorage.ts
@@ -1346,7 +1346,7 @@ export default class TransactionStorage {
];
case TransactionInErrorType.LOW_CONSUMPTION:
return [
- { $match: { 'stop.totalConsumptionWh': { $gt: 0, $lt: 1000 } } },
+ { $match: { 'stop.totalConsumptionWh': { $gt: 0, $lt: Constants.AFIREV_MINIMAL_CONSUMPTION_THRESHOLD } } },
{ $addFields: { 'errorCode': TransactionInErrorType.LOW_CONSUMPTION } }
];
case TransactionInErrorType.NEGATIVE_ACTIVITY:
@@ -1368,7 +1368,7 @@ export default class TransactionStorage {
];
case TransactionInErrorType.LOW_DURATION:
return [
- { $match: { 'stop.totalDurationSecs': { $gte: 0, $lt: 60 } } },
+ { $match: { 'stop.totalDurationSecs': { $gte: 0, $lt: Constants.AFIREV_MINIMAL_DURATION_THRESHOLD } } },
{ $addFields: { 'errorCode': TransactionInErrorType.LOW_DURATION } }
];
case TransactionInErrorType.INVALID_START_DATE:

MODIFY
src/utils/Constants.ts
src/utils/Constants.ts
@@ -499,4 +499,7 @@ export default class Constants {
...Constants.OCPP_POWER_ACTIVE_IMPORT_ATTRIBUTE,
phase: OCPPPhase.L3,
});
+
+ public static readonly AFIREV_MINIMAL_DURATION_THRESHOLD = 120; // Minimal duration - 2 minutes
+ public static readonly AFIREV_MINIMAL_CONSUMPTION_THRESHOLD = 500; // Minimal consumption - 0.5 kW.h
}

billing - afirev - threadhold