diff --git a/common/index.js b/common/index.js index 6c7436e4..9a7afec7 100644 --- a/common/index.js +++ b/common/index.js @@ -1,5 +1,5 @@ const utils = require('../utils'); -const number_format = require('../utils/number_format'); +const numberFormat = require('../utils/numberFormat'); const format = require('../utils/format'); const errors = require('./errors'); const BaseDocument = require('frappejs/model/document'); @@ -8,7 +8,7 @@ const BaseMeta = require('frappejs/model/meta'); module.exports = { initLibs(frappe) { Object.assign(frappe, utils); - Object.assign(frappe, number_format); + Object.assign(frappe, numberFormat); Object.assign(frappe, format); frappe.errors = errors; frappe.BaseDocument = BaseDocument; diff --git a/index.js b/index.js index fea81439..252a3626 100644 --- a/index.js +++ b/index.js @@ -1,9 +1,11 @@ +const Observable = require('./utils/observable'); module.exports = { async init() { if (this._initialized) return; this.initConfig(); this.initGlobals(); + this.docs = new Observable(); this._initialized = true; }, diff --git a/tests/test_utils.js b/tests/test_utils.js index 4a505c84..0dc51f9a 100644 --- a/tests/test_utils.js +++ b/tests/test_utils.js @@ -1,35 +1,35 @@ -const number_format = require('frappejs/utils/number_format'); +const numberFormat = require('frappejs/utils/numberFormat'); const assert = require('assert'); describe('Number Formatting', () => { it('should format numbers', () => { - assert.equal(number_format.format_number(100), '100.00'); - assert.equal(number_format.format_number(1000), '1,000.00'); - assert.equal(number_format.format_number(10000), '10,000.00'); - assert.equal(number_format.format_number(100000), '100,000.00'); - assert.equal(number_format.format_number(1000000), '1,000,000.00'); - assert.equal(number_format.format_number(100.1234), '100.12'); - assert.equal(number_format.format_number(1000.1234), '1,000.12'); + assert.equal(numberFormat.formatNumber(100), '100.00'); + assert.equal(numberFormat.formatNumber(1000), '1,000.00'); + assert.equal(numberFormat.formatNumber(10000), '10,000.00'); + assert.equal(numberFormat.formatNumber(100000), '100,000.00'); + assert.equal(numberFormat.formatNumber(1000000), '1,000,000.00'); + assert.equal(numberFormat.formatNumber(100.1234), '100.12'); + assert.equal(numberFormat.formatNumber(1000.1234), '1,000.12'); }); it('should parse numbers', () => { - assert.equal(number_format.parse_number('100.00'), 100); - assert.equal(number_format.parse_number('1,000.00'), 1000); - assert.equal(number_format.parse_number('10,000.00'), 10000); - assert.equal(number_format.parse_number('100,000.00'), 100000); - assert.equal(number_format.parse_number('1,000,000.00'), 1000000); - assert.equal(number_format.parse_number('100.1234'), 100.1234); - assert.equal(number_format.parse_number('1,000.1234'), 1000.1234); + assert.equal(numberFormat.parseNumber('100.00'), 100); + assert.equal(numberFormat.parseNumber('1,000.00'), 1000); + assert.equal(numberFormat.parseNumber('10,000.00'), 10000); + assert.equal(numberFormat.parseNumber('100,000.00'), 100000); + assert.equal(numberFormat.parseNumber('1,000,000.00'), 1000000); + assert.equal(numberFormat.parseNumber('100.1234'), 100.1234); + assert.equal(numberFormat.parseNumber('1,000.1234'), 1000.1234); }); it('should format lakhs and crores', () => { - assert.equal(number_format.format_number(100, '#,##,###.##'), '100.00'); - assert.equal(number_format.format_number(1000, '#,##,###.##'), '1,000.00'); - assert.equal(number_format.format_number(10000, '#,##,###.##'), '10,000.00'); - assert.equal(number_format.format_number(100000, '#,##,###.##'), '1,00,000.00'); - assert.equal(number_format.format_number(1000000, '#,##,###.##'), '10,00,000.00'); - assert.equal(number_format.format_number(10000000, '#,##,###.##'), '1,00,00,000.00'); - assert.equal(number_format.format_number(100.1234, '#,##,###.##'), '100.12'); - assert.equal(number_format.format_number(1000.1234, '#,##,###.##'), '1,000.12'); + assert.equal(numberFormat.formatNumber(100, '#,##,###.##'), '100.00'); + assert.equal(numberFormat.formatNumber(1000, '#,##,###.##'), '1,000.00'); + assert.equal(numberFormat.formatNumber(10000, '#,##,###.##'), '10,000.00'); + assert.equal(numberFormat.formatNumber(100000, '#,##,###.##'), '1,00,000.00'); + assert.equal(numberFormat.formatNumber(1000000, '#,##,###.##'), '10,00,000.00'); + assert.equal(numberFormat.formatNumber(10000000, '#,##,###.##'), '1,00,00,000.00'); + assert.equal(numberFormat.formatNumber(100.1234, '#,##,###.##'), '100.12'); + assert.equal(numberFormat.formatNumber(1000.1234, '#,##,###.##'), '1,000.12'); }); }); \ No newline at end of file diff --git a/utils/format.js b/utils/format.js index 2fb84c1f..95f55fc8 100644 --- a/utils/format.js +++ b/utils/format.js @@ -1,4 +1,4 @@ -const number_format = require('./number_format'); +const numberFormat = require('./numberFormat'); const markdown = new (require('showdown').Converter)(); const moment = require('moment'); const frappe = require('frappejs'); @@ -10,7 +10,7 @@ module.exports = { } if (field.fieldtype === 'Currency') { - value = number_format.format_number(value); + value = numberFormat.formatNumber(value); } else if (field.fieldtype === 'Text') { value = markdown.makeHtml(value || ''); diff --git a/utils/numberFormat.js b/utils/numberFormat.js new file mode 100644 index 00000000..24b40735 --- /dev/null +++ b/utils/numberFormat.js @@ -0,0 +1,104 @@ +const numberFormats = { + "#,###.##": { fractionSep: ".", groupSep: ",", precision: 2 }, + "#.###,##": { fractionSep: ",", groupSep: ".", precision: 2 }, + "# ###.##": { fractionSep: ".", groupSep: " ", precision: 2 }, + "# ###,##": { fractionSep: ",", groupSep: " ", precision: 2 }, + "#'###.##": { fractionSep: ".", groupSep: "'", precision: 2 }, + "#, ###.##": { fractionSep: ".", groupSep: ", ", precision: 2 }, + "#,##,###.##": { fractionSep: ".", groupSep: ",", precision: 2 }, + "#,###.###": { fractionSep: ".", groupSep: ",", precision: 3 }, + "#.###": { fractionSep: "", groupSep: ".", precision: 0 }, + "#,###": { fractionSep: "", groupSep: ",", precision: 0 }, +} + +module.exports = { + // parse a formatted number string + // from "4,555,000.34" -> 4555000.34 + parseNumber(number, format = '#,###.##') { + if (!number) { + return 0; + } + if (typeof number === 'number') { + return number; + } + const info = this.getFormatInfo(format); + return parseFloat(this.removeSeparator(number, info.groupSep)); + }, + + formatNumber(number, format = '#,###.##', precision = null) { + if (!number) { + number = 0; + } + let info = this.getFormatInfo(format); + if (precision) { + info.precision = precision; + } + let is_negative = false; + + number = this.parseNumber(number); + if (number < 0) { + is_negative = true; + } + number = Math.abs(number); + number = number.toFixed(info.precision); + + var parts = number.split('.'); + + // get group position and parts + var group_position = info.groupSep ? 3 : 0; + + if (group_position) { + var integer = parts[0]; + var str = ''; + + for (var i = integer.length; i >= 0; i--) { + var l = this.removeSeparator(str, info.groupSep).length; + if (format == "#,##,###.##" && str.indexOf(",") != -1) { // INR + group_position = 2; + l += 1; + } + + str += integer.charAt(i); + + if (l && !((l + 1) % group_position) && i != 0) { + str += info.groupSep; + } + } + parts[0] = str.split("").reverse().join(""); + } + if (parts[0] + "" == "") { + parts[0] = "0"; + } + + // join decimal + parts[1] = (parts[1] && info.fractionSep) ? (info.fractionSep + parts[1]) : ""; + + // join + return (is_negative ? "-" : "") + parts[0] + parts[1]; + }, + + getFormatInfo(format) { + let format_info = numberFormats[format]; + + if (!format_info) { + throw `Unknown number format "${format}"`; + } + + return format_info; + }, + + round(num, precision) { + var is_negative = num < 0 ? true : false; + var d = parseInt(precision || 0); + 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 is_negative ? -r : r; + }, + + removeSeparator(text, sep) { + return text.replace(new RegExp(sep === "." ? "\\." : sep, "g"), ''); + } +}; diff --git a/utils/number_format.js b/utils/number_format.js deleted file mode 100644 index 95839dca..00000000 --- a/utils/number_format.js +++ /dev/null @@ -1,104 +0,0 @@ -const number_formats = { - "#,###.##": { fraction_sep: ".", group_sep: ",", precision: 2 }, - "#.###,##": { fraction_sep: ",", group_sep: ".", precision: 2 }, - "# ###.##": { fraction_sep: ".", group_sep: " ", precision: 2 }, - "# ###,##": { fraction_sep: ",", group_sep: " ", precision: 2 }, - "#'###.##": { fraction_sep: ".", group_sep: "'", precision: 2 }, - "#, ###.##": { fraction_sep: ".", group_sep: ", ", precision: 2 }, - "#,##,###.##": { fraction_sep: ".", group_sep: ",", precision: 2 }, - "#,###.###": { fraction_sep: ".", group_sep: ",", precision: 3 }, - "#.###": { fraction_sep: "", group_sep: ".", precision: 0 }, - "#,###": { fraction_sep: "", group_sep: ",", precision: 0 }, -} - -module.exports = { - // parse a formatted number string - // from "4,555,000.34" -> 4555000.34 - parse_number(number, format='#,###.##') { - if (!number) { - return 0; - } - if (typeof number === 'number') { - return number; - } - const info = this.get_format_info(format); - return parseFloat(this.remove_separator(number, info.group_sep)); - }, - - format_number(number, format = '#,###.##', precision = null) { - if (!number) { - number = 0; - } - let info = this.get_format_info(format); - if (precision) { - info.precision = precision; - } - let is_negative = false; - - number = this.parse_number(number); - if (number < 0) { - is_negative = true; - } - number = Math.abs(number); - number = number.toFixed(info.precision); - - var parts = number.split('.'); - - // get group position and parts - var group_position = info.group_sep ? 3 : 0; - - if (group_position) { - var integer = parts[0]; - var str = ''; - var offset = integer.length % group_position; - for (var i = integer.length; i >= 0; i--) { - var l = this.remove_separator(str, info.group_sep).length; - if (format == "#,##,###.##" && str.indexOf(",") != -1) { // INR - group_position = 2; - l += 1; - } - - str += integer.charAt(i); - - if (l && !((l + 1) % group_position) && i != 0) { - str += info.group_sep; - } - } - parts[0] = str.split("").reverse().join(""); - } - if (parts[0] + "" == "") { - parts[0] = "0"; - } - - // join decimal - parts[1] = (parts[1] && info.fraction_sep) ? (info.fraction_sep + parts[1]) : ""; - - // join - return (is_negative ? "-" : "") + parts[0] + parts[1]; - }, - - get_format_info(format) { - let format_info = number_formats[format]; - - if (!format_info) { - throw `Unknown number format "${format}"`; - } - - return format_info; - }, - - round(num, precision) { - var is_negative = num < 0 ? true : false; - var d = parseInt(precision || 0); - 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 is_negative ? -r : r; - }, - - remove_separator(text, sep) { - return text.replace(new RegExp(sep === "." ? "\\." : sep, "g"), ''); - } -};