import { Injectable } from "@nestjs/common"; import { Decimal } from 'decimal.js'; /* * Service pour les opérations sur la finance ou les nombres décimaux * @service FinancesService */ @Injectable() export class FinancesService{ /** * le traitement de l'adition de deux nombres décimaux * @param {Decimal} a - le premier nombre * @param {Decimal} b - le deuxième nombre * @returns {Decimal} La somme de ces deux nombres */ add(a: Decimal, b: Decimal): Decimal { return a.plus(b); } /** * Le traitement de la soustraction de deux nombres décimaux * @param {Decimal} a - le premier nombre * @param {Decimal} b - le deuxième nombre * @returns {Decimal} La soustration de ces deux nombres */ subtract(a: Decimal, b: Decimal): Decimal { return a.minus(b); } /** * Le traitement de la multiplication de deux nombres décimaux * @param {Decimal} a - le premier nombre * @param {Decimal} b - le deuxième nombre * @returns {Decimal} Le produit de ces deux nombres */ multiply(a: Decimal, b: Decimal): Decimal { return a.mul(b); } /** * Division sécurisée avec gestion des erreurs et précision configurable * @param {Decimal} dividend - le dividende * @param {Decimal} divisor - le diviseur * @param {number} decimalPlaces Nombre de décimales (par défaut: 2 pour les montants financiers) * @throws {Decimal} le quotient de ce deux nombres ou Error si division par zéro */ divide(dividend: Decimal, divisor: Decimal, decimalPlaces: number = 2): Decimal { if (divisor.equals(0)) { throw new Error('Division par zéro non autorisée'); } return dividend.dividedBy(divisor).toDecimalPlaces(decimalPlaces); } /** * * @param {Decimal} dividend - le dividende * @param {Decimal} divisor - le diviseur * @param {number} decimalPlaces - le nombre de chiffres après la virgule * @returns {Decimal} - Le quotient de ces deux nombres ou l'erreur */ divideWithBankersRounding( dividend: Decimal, divisor: Decimal, decimalPlaces: number = 2 ): Decimal { if (divisor.equals(0)) { throw new Error('Division par zéro non autorisée'); } return dividend.dividedBy(divisor) .toDecimalPlaces(decimalPlaces, Decimal.ROUND_HALF_EVEN); } /** * La comparaison pour déterminer la valeur la plus grande de ces deux nombres * @param {Decimal} a - le premier nombre * @param {Decimal} b - le deuxième nombre * @returns {boolean} - la réponse */ isGreaterThan(a: Decimal, b: Decimal): boolean { return a.greaterThan(b); } /** * La comparaison pour déterminer la valeur la plus petite de ces deux nombres * @param {Decimal} a - le premier nombre * @param {Decimal} b - le deuxième nombre * @returns {boolean} - la réponse */ isLessThan(a: Decimal, b: Decimal): boolean { return a.lessThan(b); } /** * La comparaison pour déterminer si les deux nombres donnés sont égaux * @param {Decimal} a - le premier nombre * @param {Decimal} b - le deuxième nombre * @returns - la réponse */ isEqualThan(a: Decimal, b: Decimal): boolean { return a.equals(b); } /** * La comparaison si un nombre est supérieur ou égal à l'autre * @param {Decimal} a - le premier nombre * @param {Decimal} b - le deuxième nombre * @returns - la réponse */ isGreaterOrEqualThan(a: Decimal, b: Decimal): boolean { return a.greaterThanOrEqualTo(b); } /** * La comparaison si un nombre est inférieur ou égal à un autre * @param {Decimal} a - le premier nombre * @param {Decimal} b - le deuxième nombre * @returns - la réponse */ isLessOrEqualThan(a: Decimal, b: Decimal): boolean { return a.lessThanOrEqualTo(b); } /** * Le test si un nombre donné, est un nombre décimal * @param {number} value - le nombre à tester * @returns {boolean} - la réponse */ isDecimal(value: number): boolean { try { const d = new Decimal(value); const entier: number = +(d.toString().split('.')[0]); // Vérifie si le nombre a une partie décimale non nulle return !d.equals(entier); } catch (e) { return false; } } /** * La conversion d'un nombre en décimal * @param {number} value - le nombre à convertir * @param {number} precision - le nombre de chiffres après la virgule * @returns {Decimal} - le nombre décimal converti */ convertToDecimal(value: number, precision: number = 2): Decimal { return new Decimal(value).toDecimalPlaces(precision); } /** * L'arrondissement d'un nombre décimal * @param {number | string} value - le nombre à arrondir * @param {number} precision - le nombre de chiffres après la virgule * @returns {Decimal} - le nombre décimal arrondi */ roundDecimal(value: number | string, precision: number = 2): Decimal { return new Decimal(value).toDecimalPlaces(precision); } /** * L'arrondissement avancé d'un nombre donné * @param {number | Decimal} value - le nombre à arrondir * @param {number} precision - le nombre de chiffres après la virgule * @param {number} codeRounding - pour la détermination de rounding à appliquer * @returns {Decimal} - le nombre arrondi */ advacedRoundDecimal(value: number | Decimal, precision: number = 2, codeRounding: number = 0): Decimal{ let rounding: any; if(codeRounding === 0) rounding = Decimal.ROUND_UP; // Vers +∞ else if(codeRounding === 1) rounding = Decimal.ROUND_DOWN; // Vers -∞ else if(codeRounding === 2) rounding = Decimal.ROUND_CEIL; // Vers +∞ else if(codeRounding === 3) rounding = Decimal.ROUND_FLOOR; // Vers -∞ else if(codeRounding === 4) rounding = Decimal.ROUND_HALF_UP; // Arrondi mathématique (0.5 vers le haut) else if(codeRounding === 5) rounding = Decimal.ROUND_HALF_DOWN; // 0.5 vers le bas else if(codeRounding === 6) rounding = Decimal.ROUND_HALF_EVEN; // Arrondi bancaire else if(codeRounding === 7) rounding = Decimal.ROUND_HALF_CEIL; else if(codeRounding === 8) rounding = Decimal.ROUND_HALF_FLOOR; return new Decimal(value).toDecimalPlaces(precision, rounding); } }