fix: added utils to format number & currency
This commit is contained in:
parent
fe78ff844c
commit
fb2303edf1
@ -126,17 +126,6 @@ export function secondsToDuration(seconds) {
|
||||
return `${hours}h ${minutes}m ${_seconds}s`
|
||||
}
|
||||
|
||||
export function formatNumberIntoCurrency(value, currency = 'INR') {
|
||||
if (value) {
|
||||
return value.toLocaleString('en-IN', {
|
||||
maximumFractionDigits: 0,
|
||||
style: 'currency',
|
||||
currency: currency ? currency : 'INR',
|
||||
})
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
export function startCase(str) {
|
||||
return str.charAt(0).toUpperCase() + str.slice(1)
|
||||
}
|
||||
|
||||
289
frontend/src/utils/numberFormat.js
Normal file
289
frontend/src/utils/numberFormat.js
Normal file
@ -0,0 +1,289 @@
|
||||
const NUMBER_FORMAT_INFO = {
|
||||
'#,###.##': { decimalStr: '.', groupSep: ',' },
|
||||
'#.###,##': { decimalStr: ',', groupSep: '.' },
|
||||
'# ###.##': { decimalStr: '.', groupSep: ' ' },
|
||||
'# ###,##': { decimalStr: ',', groupSep: ' ' },
|
||||
"#'###.##": { decimalStr: '.', groupSep: "'" },
|
||||
'#, ###.##': { decimalStr: '.', groupSep: ', ' },
|
||||
'#,##,###.##': { decimalStr: '.', groupSep: ',' },
|
||||
'#,###.###': { decimalStr: '.', groupSep: ',' },
|
||||
'#.###': { decimalStr: '', groupSep: '.' },
|
||||
'#,###': { decimalStr: '', groupSep: ',' },
|
||||
}
|
||||
|
||||
export function replaceAll(s, t1, t2) {
|
||||
return s.split(t1).join(t2)
|
||||
}
|
||||
|
||||
export function strip(s, chars) {
|
||||
if (s) {
|
||||
s = lstrip(s, chars)
|
||||
s = rstrip(s, chars)
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
export function lstrip(s, chars) {
|
||||
if (!chars) chars = ['\n', '\t', ' ']
|
||||
// strip left
|
||||
let first_char = s.substr(0, 1)
|
||||
while (chars.includes(first_char)) {
|
||||
s = s.substr(1)
|
||||
first_char = s.substr(0, 1)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
export function rstrip(s, chars) {
|
||||
if (!chars) chars = ['\n', '\t', ' ']
|
||||
let last_char = s.substr(s.length - 1)
|
||||
while (chars.includes(last_char)) {
|
||||
s = s.substr(0, s.length - 1)
|
||||
last_char = s.substr(s.length - 1)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
export function cstr(s) {
|
||||
if (s == null) return ''
|
||||
return s + ''
|
||||
}
|
||||
|
||||
export function cint(v, def) {
|
||||
if (v === true) return 1
|
||||
if (v === false) return 0
|
||||
v = v + ''
|
||||
if (v !== '0') v = lstrip(v, ['0'])
|
||||
v = parseInt(v) // eslint-ignore-line
|
||||
if (isNaN(v)) v = def === undefined ? 0 : def
|
||||
return v
|
||||
}
|
||||
|
||||
function flt(v, decimals, numberFormat, roundingMethod) {
|
||||
if (v == null || v == '') return 0
|
||||
|
||||
if (typeof v !== 'number') {
|
||||
v = v + ''
|
||||
|
||||
// strip currency symbol if exists
|
||||
if (v.indexOf(' ') != -1) {
|
||||
// using slice(1).join(" ") because space could also be a group separator
|
||||
var parts = v.split(' ')
|
||||
v = isNaN(parseFloat(parts[0]))
|
||||
? parts.slice(parts.length - 1).join(' ')
|
||||
: v
|
||||
}
|
||||
|
||||
v = stripNumberGroups(v, numberFormat)
|
||||
|
||||
v = parseFloat(v)
|
||||
if (isNaN(v)) v = 0
|
||||
}
|
||||
|
||||
if (decimals != null) return roundNumber(v, decimals, roundingMethod)
|
||||
return v
|
||||
}
|
||||
|
||||
function stripNumberGroups(v, numberFormat) {
|
||||
if (!numberFormat) numberFormat = getNumberFormat()
|
||||
var info = getNumberFormatInfo(numberFormat)
|
||||
|
||||
// strip groups (,)
|
||||
var groupRegex = new RegExp(
|
||||
info.groupSep === '.' ? '\\.' : info.groupSep,
|
||||
'g',
|
||||
)
|
||||
v = v.replace(groupRegex, '')
|
||||
|
||||
// replace decimal separator with (.)
|
||||
if (info.decimalStr !== '.' && info.decimalStr !== '') {
|
||||
var decimal_regex = new RegExp(info.decimalStr, 'g')
|
||||
v = v.replace(decimal_regex, '.')
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
export function formatNumber(v, format, decimals) {
|
||||
if (!format) {
|
||||
format = getNumberFormat()
|
||||
if (decimals == null)
|
||||
decimals = cint(window.sysdefaults.float_precision || 3)
|
||||
}
|
||||
|
||||
let info = getNumberFormatInfo(format)
|
||||
|
||||
// Fix the decimal first, toFixed will auto fill trailing zero.
|
||||
if (decimals == null) decimals = info.precision
|
||||
|
||||
v = flt(v, decimals, format)
|
||||
|
||||
let isNegative = false
|
||||
if (v < 0) isNegative = true
|
||||
v = Math.abs(v)
|
||||
|
||||
v = v.toFixed(decimals)
|
||||
|
||||
let part = v.split('.')
|
||||
|
||||
// get group position and parts
|
||||
let groupPosition = info.groupSep ? 3 : 0
|
||||
|
||||
if (groupPosition) {
|
||||
let integer = part[0]
|
||||
let str = ''
|
||||
for (let i = integer.length; i >= 0; i--) {
|
||||
let l = replaceAll(str, info.groupSep, '').length
|
||||
if (format == '#,##,###.##' && str.indexOf(',') != -1) {
|
||||
// INR
|
||||
groupPosition = 2
|
||||
l += 1
|
||||
}
|
||||
|
||||
str += integer.charAt(i)
|
||||
|
||||
if (l && !((l + 1) % groupPosition) && i != 0) {
|
||||
str += info.groupSep
|
||||
}
|
||||
}
|
||||
part[0] = str.split('').reverse().join('')
|
||||
}
|
||||
if (part[0] + '' == '') {
|
||||
part[0] = '0'
|
||||
}
|
||||
|
||||
// join decimal
|
||||
part[1] = part[1] && info.decimalStr ? info.decimalStr + part[1] : ''
|
||||
|
||||
// join
|
||||
return (isNegative ? '-' : '') + part[0] + part[1]
|
||||
}
|
||||
|
||||
export function formatCurrency(value, df, currency = 'USD') {
|
||||
if (!value || !df) return ''
|
||||
|
||||
let precision
|
||||
if (typeof df.precision == 'number') {
|
||||
precision = df.precision
|
||||
} else {
|
||||
precision = cint(df.precision || window.sysdefaults.currency_precision || 2)
|
||||
}
|
||||
|
||||
// If you change anything below, it's going to hurt a company in UAE, a bit.
|
||||
if (precision > 2) {
|
||||
let parts = cstr(value).split('.') // should be minimum 2, comes from the DB
|
||||
let decimals = parts.length > 1 ? parts[1] : '' // parts.length == 2 ???
|
||||
|
||||
if (decimals.length < 3 || decimals.length < precision) {
|
||||
const fraction = 100
|
||||
|
||||
if (decimals.length < cstr(fraction).length) {
|
||||
precision = cstr(fraction).length - 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
value = value == null || value === '' ? '' : value
|
||||
|
||||
let format = getNumberFormat()
|
||||
let symbol = getCurrencySymbol(currency)
|
||||
|
||||
if (symbol) {
|
||||
return __(symbol) + ' ' + formatNumber(value, format, precision)
|
||||
}
|
||||
|
||||
return formatNumber(value, format, precision)
|
||||
}
|
||||
|
||||
function getCurrencySymbol(currencyCode) {
|
||||
try {
|
||||
const formatter = new Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
currency: currencyCode,
|
||||
minimumFractionDigits: 0,
|
||||
maximumFractionDigits: 0,
|
||||
})
|
||||
// Extract the currency symbol from the formatted string
|
||||
const parts = formatter.formatToParts(1)
|
||||
const symbol = parts.find((part) => part.type === 'currency')
|
||||
return symbol ? symbol.value : null
|
||||
} catch (error) {
|
||||
console.error(`Invalid currency code: ${currencyCode}`)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
function getNumberFormat() {
|
||||
return window.sysdefaults.number_format || '#,###.##'
|
||||
}
|
||||
|
||||
function getNumberFormatInfo(format) {
|
||||
let info = NUMBER_FORMAT_INFO[format]
|
||||
|
||||
if (!info) {
|
||||
info = { decimalStr: '.', groupSep: ',' }
|
||||
}
|
||||
|
||||
// get the precision from the number format
|
||||
info.precision = format.split(info.decimalStr).slice(1)[0].length
|
||||
|
||||
return info
|
||||
}
|
||||
|
||||
function roundNumber(num, precision, roundingMethod) {
|
||||
roundingMethod =
|
||||
roundingMethod ||
|
||||
window.sysdefaults.rounding_method ||
|
||||
"Banker's Rounding (legacy)"
|
||||
|
||||
let isNegative = num < 0 ? true : false
|
||||
|
||||
if (roundingMethod == "Banker's Rounding (legacy)") {
|
||||
var d = cint(precision)
|
||||
var m = Math.pow(10, d)
|
||||
var n = +(d ? Math.abs(num) * m : Math.abs(num)).toFixed(8) // Avoid rounding errors
|
||||
var i = Math.floor(n),
|
||||
f = n - i
|
||||
var r = !precision && f == 0.5 ? (i % 2 == 0 ? i : i + 1) : Math.round(n)
|
||||
r = d ? r / m : r
|
||||
return isNegative ? -r : r
|
||||
} else if (roundingMethod == "Banker's Rounding") {
|
||||
if (num == 0) return 0.0
|
||||
precision = cint(precision)
|
||||
|
||||
let multiplier = Math.pow(10, precision)
|
||||
num = Math.abs(num) * multiplier
|
||||
|
||||
let floorNum = Math.floor(num)
|
||||
let decimalPart = num - floorNum
|
||||
|
||||
// For explanation of this method read python flt implementation notes.
|
||||
let epsilon = 2.0 ** (Math.log2(Math.abs(num)) - 52.0)
|
||||
|
||||
if (Math.abs(decimalPart - 0.5) < epsilon) {
|
||||
num = floorNum % 2 == 0 ? floorNum : floorNum + 1
|
||||
} else {
|
||||
num = Math.round(num)
|
||||
}
|
||||
num = num / multiplier
|
||||
return isNegative ? -num : num
|
||||
} else if (roundingMethod == 'Commercial Rounding') {
|
||||
if (num == 0) return 0.0
|
||||
|
||||
let digits = cint(precision)
|
||||
let multiplier = Math.pow(10, digits)
|
||||
|
||||
num = num * multiplier
|
||||
|
||||
// For explanation of this method read python flt implementation notes.
|
||||
let epsilon = 2.0 ** (Math.log2(Math.abs(num)) - 52.0)
|
||||
if (isNegative) {
|
||||
epsilon = -1 * epsilon
|
||||
}
|
||||
|
||||
num = Math.round(num + epsilon)
|
||||
return num / multiplier
|
||||
} else {
|
||||
throw new Error(`Unknown rounding method ${roundingMethod}`)
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user