2
0
mirror of https://github.com/frappe/books.git synced 2024-11-10 07:40:55 +00:00

added new controls float, currency and added formatting

This commit is contained in:
Rushabh Mehta 2018-01-25 15:34:48 +05:30
parent a53cf5a264
commit ec0ebd95a5
13 changed files with 228 additions and 37 deletions

View File

@ -51,7 +51,9 @@ module.exports = class Desk {
frappe.router.add('new/:doctype', async (params) => {
let doc = await frappe.get_new_doc(params.doctype);
frappe.router.set_route('edit', doc.doctype, doc.name);
// unset the name, its local
await frappe.router.set_route('edit', doc.doctype, doc.name);
await doc.set('name', '');
});
}

View File

@ -15,11 +15,6 @@ class BaseControl {
bind(doc) {
this.doc = doc;
this.doc.add_handler(this.fieldname, () => {
this.set_doc_value();
});
this.set_doc_value();
}
@ -56,6 +51,7 @@ class BaseControl {
make_input() {
this.input = frappe.ui.add('input', 'form-control', this.form_group);
this.input.setAttribute('autocomplete', 'off');
}
set_input_name() {
@ -70,16 +66,24 @@ class BaseControl {
}
set_input_value(value) {
this.input.value = this.format(value);
}
format(value) {
if (value === undefined || value === null) {
value = '';
}
this.input.value = value;
return value;
}
async get_input_value() {
async get_parsed_value() {
return await this.parse(this.input.value);
}
get_input_value() {
return this.input.value;
}
async parse(value) {
return value;
}
@ -93,9 +97,11 @@ class BaseControl {
}
async handle_change(e) {
let value = await this.get_input_value();
let value = await this.parse(this.get_input_value());
value = await this.validate(value);
await this.doc.set(this.fieldname, value);
if (this.doc[this.fieldname] !== value) {
await this.doc.set(this.fieldname, value);
}
}
disable() {

View File

@ -0,0 +1,13 @@
const FloatControl = require('./float');
const frappe = require('frappejs');
class CurrencyControl extends FloatControl {
parse(value) {
return frappe.parse_number(value);
}
format(value) {
return frappe.format_number(value);
}
};
module.exports = CurrencyControl;

View File

@ -0,0 +1,11 @@
const BaseControl = require('./base');
class FloatControl extends BaseControl {
make() {
super.make();
this.input.setAttribute('type', 'text');
this.input.classList.add('text-right');
}
};
module.exports = FloatControl;

View File

@ -2,10 +2,11 @@ const control_classes = {
Data: require('./data'),
Text: require('./text'),
Select: require('./select'),
Link: require('./link')
Link: require('./link'),
Float: require('./float'),
Currency: require('./currency')
}
module.exports = {
get_control_class(fieldtype) {
return control_classes[fieldtype];

View File

@ -80,6 +80,14 @@ module.exports = class BaseForm {
for (let control of this.controls_list) {
control.bind(this.doc);
}
// refresh value in control
this.doc.add_handler('change', (params) => {
let control = this.controls[params.fieldname];
if (control && control.get_input_value() !== control.format(params.fieldname)) {
control.set_doc_value();
}
});
}
async submit() {

View File

@ -119,7 +119,7 @@ module.exports = class BaseList {
}
get_row_html(data) {
return `<a href="#edit/${this.doctype}/${data.name}>${data.name}</a>`;
return `<a href="#edit/${this.doctype}/${data.name}">${data.name}</a>`;
}
get_row(i) {

View File

@ -22,13 +22,14 @@ module.exports = class BaseDocument {
this.handlers[key].push(method || key);
}
get(key) {
return this[key];
get(fieldname) {
return this[fieldname];
}
set(key, value) {
this.validate_field(key, value);
this[key] = value;
// set value and trigger change
async set(fieldname, value) {
this[fieldname] = await this.validate_field(fieldname, value);
await this.trigger('change', {doc: this, fieldname: fieldname, value: value});
}
set_name() {
@ -69,11 +70,12 @@ module.exports = class BaseDocument {
}
}
validate_field (key, value) {
async validate_field (key, value) {
let df = this.meta.get_field(key);
if (df.fieldtype=='Select') {
this.meta.validate_select(df, value);
return this.meta.validate_select(df, value);
}
return value;
}
get_valid_dict() {
@ -110,9 +112,11 @@ module.exports = class BaseDocument {
this.set_name();
this.set_standard_values();
this.set_keywords();
await this.trigger('validate', 'before_insert');
await this.trigger('validate');
await this.trigger('before_insert');
await frappe.db.insert(this.doctype, this.get_valid_dict());
await this.trigger('after_insert', 'after_save');
await this.trigger('after_insert');
await this.trigger('after_save');
}
async delete() {
@ -121,15 +125,13 @@ module.exports = class BaseDocument {
await this.trigger('after_delete');
}
async trigger() {
for(var key of arguments) {
if (this.handlers[key]) {
for (let method of this.handlers[key]) {
if (typeof method === 'string') {
await this[method]();
} else {
await method(this);
}
async trigger(key, params) {
if (this.handlers[key]) {
for (let method of this.handlers[key]) {
if (typeof method === 'string') {
await this[method](params);
} else {
await method(params);
}
}
}
@ -138,9 +140,11 @@ module.exports = class BaseDocument {
async update() {
this.set_standard_values();
this.set_keywords();
await this.trigger('validate', 'before_update');
await this.trigger('validate');
await this.trigger('before_update');
await frappe.db.update(this.doctype, this.get_valid_dict());
await this.trigger('after_update', 'after_save');
await this.trigger('after_update');
await this.trigger('after_save');
return this;
}
};

View File

@ -85,10 +85,10 @@ module.exports = class BaseMeta extends BaseDocument {
if (!options.includes(value)) {
throw new frappe.errors.ValueError(`${value} must be one of ${options.join(", ")}`);
}
return value;
}
async trigger(key, event = {}) {
Object.assign(event, {
doc: this,
name: key

View File

@ -6,7 +6,8 @@ class ToDoList extends BaseList {
return ['name', 'subject', 'status'];
}
get_row_html(data) {
return `<a href="#edit/todo/${data.name}">${data.subject}</a>`;
let symbol = data.status=="Closed" ? "✔" : "";
return `<a href="#edit/todo/${data.name}">${symbol} ${data.subject}</a>`;
}
}

35
tests/test_utils.js Normal file
View File

@ -0,0 +1,35 @@
const utils = require('frappejs/utils');
const assert = require('assert');
describe('Number Formatting', () => {
it('should format numbers', () => {
assert.equal(utils.format_number(100), '100.00');
assert.equal(utils.format_number(1000), '1,000.00');
assert.equal(utils.format_number(10000), '10,000.00');
assert.equal(utils.format_number(100000), '100,000.00');
assert.equal(utils.format_number(1000000), '1,000,000.00');
assert.equal(utils.format_number(100.1234), '100.12');
assert.equal(utils.format_number(1000.1234), '1,000.12');
});
it('should parse numbers', () => {
assert.equal(utils.parse_number('100.00'), 100);
assert.equal(utils.parse_number('1,000.00'), 1000);
assert.equal(utils.parse_number('10,000.00'), 10000);
assert.equal(utils.parse_number('100,000.00'), 100000);
assert.equal(utils.parse_number('1,000,000.00'), 1000000);
assert.equal(utils.parse_number('100.1234'), 100.1234);
assert.equal(utils.parse_number('1,000.1234'), 1000.1234);
});
it('should format lakhs and crores', () => {
assert.equal(utils.format_number(100, '#,##,###.##'), '100.00');
assert.equal(utils.format_number(1000, '#,##,###.##'), '1,000.00');
assert.equal(utils.format_number(10000, '#,##,###.##'), '10,000.00');
assert.equal(utils.format_number(100000, '#,##,###.##'), '1,00,000.00');
assert.equal(utils.format_number(1000000, '#,##,###.##'), '10,00,000.00');
assert.equal(utils.format_number(10000000, '#,##,###.##'), '1,00,00,000.00');
assert.equal(utils.format_number(100.1234, '#,##,###.##'), '100.12');
assert.equal(utils.format_number(1000.1234, '#,##,###.##'), '1,000.12');
});
});

View File

@ -1,4 +1,8 @@
module.exports = {
let utils = {};
Object.assign(utils, require('./number_format'));
Object.assign(utils, {
slug(text) {
return text.toLowerCase().replace(/ /g, '_');
},
@ -17,4 +21,6 @@ module.exports = {
setTimeout(resolve, seconds * 1000);
});
}
}
});
module.exports = utils;

104
utils/number_format.js Normal file
View File

@ -0,0 +1,104 @@
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"), '');
}
};