UNPKG

carbone

Version:

Fast, Simple and Powerful report generator. Injects JSON and produces PDF, DOCX, XLSX, ODT, PPTX, ODS, ...!

319 lines (302 loc) 9.97 kB
const locale = require('./_locale.js'); const currency = require('./_currency.js'); /** * Convert from one currency to another * * Exchange rates are included by default in Carbone but you can provide a new echange rate * for one report in `options.currencyRates` of `Carbone.render` or globally with `Carbone.set` * * `convCurr()` without parameters converts automatically from `options.currencySource` to `options.currencyTarget`. * * @version 1.2.0 * * @exampleContext {"currency": { "source":"EUR", "target":"USD", "rates": { "EUR":1, "USD":2 }} } * @example [10 ] * @example [1000 ] * @example [1000, "EUR" ] * @example [1000, "USD" ] * @example [1000, "USD", "USD" ] * * @param {Number} d Number to convert * @param {String} target [optional] convert to this currency ('EUR'). By default it equals `options.currencyTarget` * @param {String} source [optional] currency of source data ('USD'). By default it equals `options.currencySource` * @return {String} return converted values */ function convCurr (d, target, source) { var _target = target ? target : this.currency.target; var _source = source ? source : this.currency.source; var _targetRate = this.currency.rates[_target] || 1; var _sourceRate = this.currency.rates[_source] || 1; var _valueInEuro = d / _sourceRate; this.modifiedCurrencyTarget = _target; return _valueInEuro * _targetRate; // raseError(this.currency[_target] === undefined, 'Unknown rate for currency target "'+_target+'"'); // raseError(this.currency[_base] === undefined , 'Unknown rate for currency base "'+_base+'"'); } /** * Round a number * * Same as toFixed(2) but it rounds number correclty `round(1.05, 1) = 1.1` * * @version 1.2.0 * * @example [10.05123 , 2 ] * @example [1.05 , 1 ] * * @param {Number} num number * @param {Number} precision number of decimal * @return {Number} */ function round (num, precision) { if (!('' + num).includes('e')) { return +(Math.round(num + 'e+' + precision) + 'e-' + precision); } else { var _arr = ('' + num).split('e'); var _sig = ''; if (+_arr[1] + precision > 0) { _sig = '+'; } return +(Math.round(+_arr[0] + 'e' + _sig + (+_arr[1] + precision)) + 'e-' + precision); } } /** * Fast method to format a number * Inspired by https://stackoverflow.com/questions/149055/how-can-i-format-numbers-as-dollars-currency-string-in-javascript * * Tested solutions (at least 5x slower to 10x slower): * - numeralJs * - https://github.com/BenjaminVanRyseghem/numbro * - http://mottie.github.io/javascript-number-formatter/ * - http://openexchangerates.github.io/accounting.js * - fastest method is native Intl.NumberFormat but locales are not included in Nodejs : https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/NumberFormat * * Other method to test and analyze * - https://jsperf.com/number-formatting-with-commas/5 * - https://stackoverflow.com/questions/2901102/how-to-print-a-number-with-commas-as-thousands-separators-in-javascript * * @private * @param {Number} value * @param {Number} value * @param {Number} precision * @return {String} */ function _format (value, format, precision = 3) { var _re = '\\d(?=(\\d{' + (format.group) + '})+' + (precision > 0 ? '\\D' : '$') + ')'; var _value = round(value, precision); var _num = _value.toFixed(Math.max(0, ~~precision)); return (format.decimal ? _num.replace('.', format.decimal) : _num).replace(new RegExp(_re, 'g'), '$&' + (format.separator || ',')); } /** * Format number according to the locale. * Applying a number of decimals depends on the report type: * - For ODS/XLSX, the number of decimals has to be formatted based on the text editor. * - For the other type of files, the number of decimals depends on the `precision` parameter passed to the formatter. * * @version 1.2.0 * * @exampleContext {"lang":"en-us"} * @example ["10" ] * @example ["1000.456" ] * * @param {Number} d Number to format * @param {Number} precision [optional] Number of decimals * @return {String} return converted values */ function formatN (d, precision) { if (d !== null && typeof d !== 'undefined') { var _locale = locale[this.lang] || locale.en; return _format(d, _locale.number, precision); } return d; } /** * * @version 1.2.0 * * Format currency numbers * * Currencies are defined by the locale (`options.lang`). It can be overwritten by * `options.currencySource` and `options.currencyTarget` for one report in `Carbone.render` * or globally with `Carbone.set` * * When `options.lang === 'fr-FR'` the `currencySource` and `currencyTarget` equals by default `EUR`. * * If the formatter `convCurr()` is used before, formatC prints the corresponding target currency used in `convCurr()`. * * By default, it prints with the currency symbol only, but you can use other output formats: * * @exampleContext {"lang":"en-us", "currency": { "source":"EUR", "target":"USD", "rates": { "EUR":1, "USD":2 }} } * @example ["1000.456" ] * @example ["1000.456", "M" ] * @example ["1" , "M" ] * @example ["1000" , "L" ] * @example ["1000" , "LL" ] * * @exampleContext {"lang":"fr-fr", "currency": { "source":"EUR", "target":"USD", "rates": { "EUR":1, "USD":2 }} } * @example ["1000.456" ] * * @exampleContext {"lang":"fr-fr", "currency": { "source":"EUR", "target":"EUR", "rates": { "EUR":1, "USD":2 }} } * @example ["1000.456" ] * * @param {Number} d Number to format * @param {Number} precisionOrFormat [optional] Number of decimal, or specific format <br> * - Integer : change default precision of the currency * - M : print Major currency name without the number * - L : prints number with currency symbol (by default) * - LL : prints number with Major currency name * @return {String} return converted values * */ function formatC (d, precisionOrFormat) { if (d !== null && typeof d !== 'undefined') { var _locale = locale[this.lang] || locale.en; // TODO optimize, this test should be done before var _currency = this.modifiedCurrencyTarget || this.currency.target; var _currencyInfo = currency[_currency]; var _precision = _currencyInfo.precision; var _customPrec = parseInt(precisionOrFormat, 10); var _formatFn = _locale.currency.L; if (!isNaN(_customPrec)) { _precision = _customPrec; } else if ( _locale.currency[precisionOrFormat] instanceof Function ) { _formatFn = _locale.currency[precisionOrFormat]; } var _valueRaw = _format(convCurr.call(this, d), _locale.number, _precision); // reset modifiedCurrencyTarget for next use this.modifiedCurrencyTarget = null; return _formatFn(_valueRaw, _currencyInfo.symbol, _currencyInfo.minSymbol, // eslint-disable-next-line eqeqeq (d != 1 ? _currencyInfo.major + 's' : _currencyInfo.major), // eslint-disable-next-line eqeqeq (d != 1 ? _currencyInfo.minor + 's' : _currencyInfo.minor), _currencyInfo.name ); } return d; } /** * Add two numbers * * @version 1.2.0 * * @example [1000.4 , 2 ] * @example ["1000.4", "2" ] * * @param {Number} value Value to add * @return {Number} Result */ function add (d, value) { if (d !== null && typeof d !== 'undefined') { return parseFloat(d) + parseFloat(value); } return d; } add.isAcceptingMathExpression = true; /** * Substract two numbers * * @version 1.2.0 * * @example [1000.4 , 2 ] * @example ["1000.4", "2" ] * * @param {Number} value Value to substract * @return {Number} Result */ function sub (d, value) { if (d !== null && typeof d !== 'undefined') { return parseFloat(d) - parseFloat(value); } return d; } sub.isAcceptingMathExpression = true; /** * Multiply two numbers * * @version 1.2.0 * * @example [1000.4 , 2 ] * @example ["1000.4", "2" ] * * @param {Number} value Value to multiply * @return {Number} Result */ function mul (d, value) { if (d !== null && typeof d !== 'undefined') { return parseFloat(d) * parseFloat(value); } return d; } mul.isAcceptingMathExpression = true; /** * Divide two numbers * * @version 1.2.0 * * @example [1000.4 , 2 ] * @example ["1000.4" , "2" ] * * @param {Number} value Value to divide * @return {Number} Result */ function div (d, value) { if (d !== null && typeof d !== 'undefined' && parseFloat(value) !== 0) { return parseFloat(d) / parseFloat(value); } return d; } div.isAcceptingMathExpression = true; module.exports = { formatN : formatN, formatC : formatC, convCurr : convCurr, round : round, add : add, sub : sub, mul : mul, div : div, /** * Converts a number to an INT * @deprecated * @version 1.0.0 deprecated * * @return {Number} */ int : function (d) { return parseInt(d, 10); }, /** * Converts a number with English specifications (decimal separator is '.') * @deprecated * @version 1.0.0 deprecated * * @return {String} */ toEN : function (d) { return (d + '').replace(/,/g, '.'); }, /** * Converts a number into string, keeping only <nb> decimals * @deprecated * @version 1.0.0 deprecated * * @param {Number} nb * @return {String} */ toFixed : function (d, nb) { return parseFloat(d).toFixed(nb); }, /** * Converts a number with French specifications (decimal separator is ',') * @deprecated * @version 1.0.0 deprecated * * @return {String} */ toFR : function (d) { return (d + '').replace(/\./g, ','); } };