Truncate the pricing instead of rounding it
MODIFY
src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/OCPIMapping.ts
src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/OCPIMapping.ts
@@ -415,7 +415,7 @@ export default class OCPIMapping {
start_date_time: moment(inactivityStart).subtract(inactivity, 'seconds').toDate(),
dimensions: [{
type: CdrDimensionType.PARKING_TIME,
- volume: Utils.roundTo(inactivity / 3600, 3)
+ volume: Utils.truncTo(inactivity / 3600, 3)
}]
});
}
@@ -749,7 +749,7 @@ export default class OCPIMapping {
if (duration > 0) {
chargingPeriod.dimensions.push({
type: CdrDimensionType.PARKING_TIME,
- volume: Utils.roundTo(duration, 3)
+ volume: Utils.truncTo(duration, 3)
});
}
}

MODIFY
src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/OCPISessionsService.ts
src/server/ocpi/ocpi-services-impl/ocpi-2.1.1/OCPISessionsService.ts
@@ -136,7 +136,7 @@ export default class OCPISessionsService {
transaction.currentTimestamp = session.last_updated;
transaction.price = session.total_cost;
transaction.priceUnit = session.currency;
- transaction.roundedPrice = Utils.roundTo(session.total_cost, 2);
+ transaction.roundedPrice = Utils.truncTo(session.total_cost, 2);
transaction.lastConsumption = {
value: session.kwh * 1000,
timestamp: session.last_updated
@@ -150,7 +150,7 @@ export default class OCPISessionsService {
price: session.total_cost,
priceUnit: session.currency,
pricingSource: 'ocpi',
- roundedPrice: Utils.roundTo(session.total_cost, 2),
+ roundedPrice: Utils.truncTo(session.total_cost, 2),
stateOfCharge: 0,
tagID: session.auth_id,
timestamp: stopTimestamp,
@@ -201,7 +201,7 @@ export default class OCPISessionsService {
}
transaction.priceUnit = cdr.currency;
transaction.price = cdr.total_cost;
- transaction.roundedPrice = Utils.roundTo(cdr.total_cost, 2);
+ transaction.roundedPrice = Utils.truncTo(cdr.total_cost, 2);
transaction.currentTimestamp = cdr.last_updated;
transaction.stop = {
extraInactivityComputed: false,
@@ -210,7 +210,7 @@ export default class OCPISessionsService {
price: cdr.total_cost,
priceUnit: cdr.currency,
pricingSource: 'ocpi',
- roundedPrice: Utils.roundTo(cdr.total_cost, 2),
+ roundedPrice: Utils.truncTo(cdr.total_cost, 2),
stateOfCharge: 0,
tagID: cdr.auth_id,
timestamp: cdr.stop_date_time,

MODIFY
src/server/ocpp/services/OCPPService.ts
src/server/ocpp/services/OCPPService.ts
@@ -1356,7 +1356,7 @@ export default class OCPPService {
module: MODULE_NAME, method: 'updateChargingStationWithTransaction',
action: ServerAction.CONSUMPTION,
user: transaction.userID,
- message: `Connector ID '${foundConnector.connectorId}' > Transaction ID '${foundConnector.currentTransactionID}' > Instant: ${Utils.roundTo(foundConnector.currentInstantWatts / 1000, 2)} kW, Total: ${Utils.roundTo(foundConnector.currentTotalConsumptionWh / 1000, 2)} kW.h${foundConnector.currentStateOfCharge ? ', SoC: ' + foundConnector.currentStateOfCharge.toString() + ' %' : ''}`
+ message: `Connector ID '${foundConnector.connectorId}' > Transaction ID '${foundConnector.currentTransactionID}' > Instant: ${Utils.truncTo(foundConnector.currentInstantWatts / 1000, 2)} kW, Total: ${Utils.truncTo(foundConnector.currentTotalConsumptionWh / 1000, 2)} kW.h${foundConnector.currentStateOfCharge ? ', SoC: ' + foundConnector.currentStateOfCharge.toString() + ' %' : ''}`
});
// Cleanup connector transaction data
} else if (foundConnector) {

MODIFY
src/server/ocpp/utils/OCPPUtils.ts
src/server/ocpp/utils/OCPPUtils.ts
@@ -218,7 +218,7 @@ export default class OCPPUtils {
if (pricedConsumption.cumulatedAmount) {
consumption.cumulatedAmount = pricedConsumption.cumulatedAmount;
} else {
- consumption.cumulatedAmount = Utils.roundTo(transaction.currentCumulatedPrice + consumption.amount, 6);
+ consumption.cumulatedAmount = Utils.truncTo(transaction.currentCumulatedPrice + consumption.amount, 6);
}
transaction.currentCumulatedPrice = consumption.cumulatedAmount;
}
@@ -236,15 +236,15 @@ export default class OCPPUtils {
if (pricedConsumption.cumulatedAmount) {
consumption.cumulatedAmount = pricedConsumption.cumulatedAmount;
} else {
- consumption.cumulatedAmount = Utils.roundTo(transaction.currentCumulatedPrice + consumption.amount, 6);
+ consumption.cumulatedAmount = Utils.truncTo(transaction.currentCumulatedPrice + consumption.amount, 6);
}
transaction.currentCumulatedPrice = consumption.cumulatedAmount;
// Update Transaction
if (!transaction.stop) {
transaction.stop = {} as TransactionStop;
}
- transaction.stop.price = Utils.roundTo(transaction.currentCumulatedPrice, 6);
- transaction.stop.roundedPrice = Utils.roundTo(transaction.currentCumulatedPrice, 2);
+ transaction.stop.price = Utils.truncTo(transaction.currentCumulatedPrice, 6);
+ transaction.stop.roundedPrice = Utils.truncTo(transaction.currentCumulatedPrice, 2);
transaction.stop.priceUnit = pricedConsumption.currencyCode;
transaction.stop.pricingSource = pricedConsumption.pricingSource;
}
@@ -469,7 +469,7 @@ export default class OCPPUtils {
}
// Check Simple Pricing
if (transaction.pricingSource === PricingSettingsType.SIMPLE) {
- transactionSimplePricePerkWh = Utils.roundTo(transaction.stop.price / (transaction.stop.totalConsumptionWh / 1000), 2);
+ transactionSimplePricePerkWh = Utils.truncTo(transaction.stop.price / (transaction.stop.totalConsumptionWh / 1000), 2);
}
// Get the Charging Station
const chargingStation = await ChargingStationStorage.getChargingStation(tenantID,

MODIFY
src/server/odata/odata-entities/ODataTransactions.ts
src/server/odata/odata-entities/ODataTransactions.ts
@@ -64,7 +64,7 @@ export default class ODataTransactions extends AbstractODataEntities {
delete transaction.stop['tagID'];
}
if (Utils.objectHasProperty(transaction.stop, 'price')) {
- transaction.stop.price = Utils.roundTo(transaction.stop.price, 15);
+ transaction.stop.price = Utils.truncTo(transaction.stop.price, 15);
}
}
return transaction;

MODIFY
src/server/rest/v1/service/StatisticService.ts
src/server/rest/v1/service/StatisticService.ts
@@ -644,7 +644,7 @@ export default class StatisticService {
`${Utils.buildUserFullName(transaction.user, false)}` + Constants.CSV_SEPARATOR;
csv += (year && year !== '0') ? `${year}` + Constants.CSV_SEPARATOR : '';
csv += (transaction._id.month > 0) ? `${transaction._id.month}` + Constants.CSV_SEPARATOR : '';
- number = Utils.roundTo(transaction.total, 2);
+ number = Utils.truncTo(transaction.total, 2);
// Use raw numbers - it makes no sense to format numbers here,
// anyway only locale 'en-US' is supported here as could be seen by:
// const supportedLocales = Intl.NumberFormat.supportedLocalesOf(['fr-FR', 'en-US', 'de-DE']);

MODIFY
src/server/rest/v1/service/TransactionService.ts
src/server/rest/v1/service/TransactionService.ts
@@ -793,7 +793,7 @@ export default class TransactionService {
csv += `${transaction.stop ? Math.round(transaction.stop.totalConsumptionWh ? transaction.stop.totalConsumptionWh / 1000 : 0) : ''}` + Constants.CSV_SEPARATOR;
csv += `${transaction.stop ? Math.round(transaction.stop.totalDurationSecs ? transaction.stop.totalDurationSecs / 60 : 0) : ''}` + Constants.CSV_SEPARATOR;
csv += `${transaction.stop ? Math.round(transaction.stop.totalInactivitySecs ? transaction.stop.totalInactivitySecs / 60 : 0) : ''}` + Constants.CSV_SEPARATOR;
- csv += `${transaction.stop ? Utils.roundTo(transaction.stop.price, 2) : ''}` + Constants.CSV_SEPARATOR;
+ csv += `${transaction.stop ? Utils.truncTo(transaction.stop.price, 2) : ''}` + Constants.CSV_SEPARATOR;
csv += `${transaction.stop ? transaction.stop.priceUnit : ''}\r\n`;
}
return csv;

MODIFY
src/utils/I18nManager.ts
src/utils/I18nManager.ts
@@ -52,7 +52,7 @@ export default class I18nManager {
if (currency) {
return new Intl.NumberFormat(this.language, { style: 'currency', currency }).format(value);
}
- return this.formatNumber(Utils.roundTo(value, 2));
+ return this.formatNumber(Utils.truncTo(value, 2));
}
public formatPercentage(value: number): string {

MODIFY
src/utils/Logging.ts
src/utils/Logging.ts
@@ -55,7 +55,7 @@ export default class Logging {
delete Logging.traceCalls[key];
found = true;
}
- const sizeOfDataKB = Utils.roundTo(sizeof(data) / 1024, 2);
+ const sizeOfDataKB = Utils.truncTo(sizeof(data) / 1024, 2);
const numberOfRecords = Array.isArray(data) ? data.length : 0;
console.debug(`${module}.${method} ${found ? '- ' + executionDurationMillis.toString() + 'ms' : ''}${!Utils.isEmptyJSon(data) ? '- ' + sizeOfDataKB.toString() + 'kB' : ''} ${Array.isArray(data) ? '- ' + numberOfRecords.toString() + 'rec' : ''}`);
if (sizeOfDataKB > Constants.PERF_MAX_DATA_VOLUME_KB) {
@@ -257,7 +257,7 @@ export default class Logging {
// Compute Length
let sizeOfDataKB = 0;
if (res.getHeader('content-length')) {
- sizeOfDataKB = Utils.roundTo(res.getHeader('content-length') as number / 1024, 2);
+ sizeOfDataKB = Utils.truncTo(res.getHeader('content-length') as number / 1024, 2);
}
if (Utils.isDevelopmentEnv()) {
console.debug(`Express HTTP Response - ${(executionDurationMillis > 0) ? executionDurationMillis : '?'}ms - ${(sizeOfDataKB > 0) ? sizeOfDataKB : '?'}kB >> ${req.method}/${res.statusCode} '${req.url}'`);
@@ -318,7 +318,7 @@ export default class Logging {
// Compute Length
let sizeOfDataKB = 0;
if (response.config.headers['Content-Length']) {
- sizeOfDataKB = Utils.roundTo(response.config.headers['Content-Length'] / 1024, 2);
+ sizeOfDataKB = Utils.truncTo(response.config.headers['Content-Length'] / 1024, 2);
}
if (Utils.isDevelopmentEnv()) {
console.log(`Axios HTTP Response - ${(executionDurationMillis > 0) ? executionDurationMillis : '?'}ms - ${(sizeOfDataKB > 0) ? sizeOfDataKB : '?'}kB << ${response.config.method.toLocaleUpperCase()}/${response.status} '${response.config.url}'`);

MODIFY
src/utils/Utils.ts
src/utils/Utils.ts
@@ -444,11 +444,11 @@ export default class Utils {
}
public static computeSimplePrice(pricePerkWh: number, consumptionWh: number): number {
- return Utils.roundTo(pricePerkWh * (consumptionWh / 1000), 6);
+ return Utils.truncTo(pricePerkWh * (consumptionWh / 1000), 6);
}
public static computeSimpleRoundedPrice(pricePerkWh: number, consumptionWh: number): number {
- return Utils.roundTo(pricePerkWh * (consumptionWh / 1000), 2);
+ return Utils.truncTo(pricePerkWh * (consumptionWh / 1000), 2);
}
public static convertUserToObjectID(user: User | UserToken | string): ObjectID | null {
@@ -972,6 +972,11 @@ export default class Utils {
return Math.round(value * roundPower) / roundPower;
}
+ public static truncTo(value: number, scale: number): number {
+ const roundPower = Math.pow(10, scale);
+ return Math.trunc(value * roundPower) / roundPower;
+ }
+
public static firstLetterInUpperCase(value: string): string {
return value[0].toUpperCase() + value.substring(1);
}

Truncate the pricing instead of rounding it