Ability to set precision per field. Format 1.0000 as 1, if fieldtype is Float.

This commit is contained in:
Anand Doshi 2014-09-05 17:13:30 +05:30
parent 09c2468318
commit 32e9241409
18 changed files with 323 additions and 201 deletions

View file

@ -62,6 +62,16 @@
"reqd": 1,
"search_index": 0
},
{
"depends_on": "eval:in_list([\"Float\", \"Currency\", \"Percent\"], doc.fieldtype)",
"description": "Set non-standard precision for a Float or Currency field",
"fieldname": "precision",
"fieldtype": "Select",
"label": "Precision",
"options": "\n1\n2\n3\n4\n5\n6",
"permlevel": 0,
"precision": ""
},
{
"fieldname": "options_help",
"fieldtype": "HTML",
@ -257,7 +267,7 @@
],
"icon": "icon-glass",
"idx": 1,
"modified": "2014-06-20 05:54:17.225853",
"modified": "2014-09-05 07:41:13.076820",
"modified_by": "Administrator",
"module": "Core",
"name": "Custom Field",

View file

@ -0,0 +1,10 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
# See license.txt
import frappe
import unittest
test_records = frappe.get_test_records('Custom Field')
class TestCustomField(unittest.TestCase):
pass

View file

@ -0,0 +1 @@
[]

View file

@ -40,7 +40,8 @@ class CustomizeForm(Document):
'allow_on_submit': 'Check',
'depends_on': 'Data',
'description': 'Text',
'default': 'Text'
'default': 'Text',
'precision': 'Select'
}
allowed_fieldtype_change = (('Currency', 'Float', 'Percent'), ('Small Text', 'Data'),

View file

@ -5,6 +5,13 @@
"docstatus": 0,
"doctype": "DocType",
"fields": [
{
"fieldname": "label_and_type",
"fieldtype": "Section Break",
"label": "Label and Type",
"permlevel": 0,
"precision": ""
},
{
"fieldname": "label",
"fieldtype": "Data",
@ -19,6 +26,7 @@
"search_index": 1
},
{
"default": "Data",
"fieldname": "fieldtype",
"fieldtype": "Select",
"hidden": 0,
@ -46,6 +54,42 @@
"reqd": 0,
"search_index": 1
},
{
"fieldname": "reqd",
"fieldtype": "Check",
"hidden": 0,
"label": "Mandatory",
"oldfieldname": "reqd",
"oldfieldtype": "Check",
"permlevel": 0,
"print_hide": 0,
"print_width": "50px",
"reqd": 0,
"search_index": 0,
"width": "50px"
},
{
"fieldname": "in_list_view",
"fieldtype": "Check",
"label": "In List View",
"permlevel": 0
},
{
"fieldname": "column_break_7",
"fieldtype": "Column Break",
"permlevel": 0,
"precision": ""
},
{
"depends_on": "eval:in_list([\"Float\", \"Currency\", \"Percent\"], doc.fieldtype)",
"description": "Set non-standard precision for a Float or Currency field",
"fieldname": "precision",
"fieldtype": "Select",
"label": "Precision",
"options": "\n1\n2\n3\n4\n5\n6",
"permlevel": 0,
"precision": ""
},
{
"description": "For Links, enter the DocType as range\nFor Select, enter list of Options separated by comma",
"fieldname": "options",
@ -60,6 +104,25 @@
"reqd": 0,
"search_index": 0
},
{
"fieldname": "permissions",
"fieldtype": "Section Break",
"label": "Permissions",
"permlevel": 0,
"precision": ""
},
{
"description": "This field will appear only if the fieldname defined here has value OR the rules are true (examples): <br>\nmyfield\neval:doc.myfield=='My Value'<br>\neval:doc.age>18",
"fieldname": "depends_on",
"fieldtype": "Data",
"hidden": 0,
"label": "Depends On",
"oldfieldname": "depends_on",
"oldfieldtype": "Data",
"permlevel": 0,
"print_hide": 0,
"reqd": 0
},
{
"default": "0",
"fieldname": "permlevel",
@ -75,35 +138,11 @@
"search_index": 0
},
{
"fieldname": "width",
"fieldtype": "Data",
"hidden": 0,
"in_list_view": 1,
"label": "Width",
"oldfieldname": "width",
"oldfieldtype": "Data",
"permlevel": 0,
"print_hide": 0,
"print_width": "50px",
"reqd": 0,
"search_index": 0,
"width": "50px"
},
{
"description": "Print Width of the field, if the field is a column in a table",
"fieldname": "print_width",
"fieldtype": "Data",
"label": "Print Width",
"permlevel": 0,
"print_width": "50px",
"width": "50px"
},
{
"fieldname": "reqd",
"fieldname": "hidden",
"fieldtype": "Check",
"hidden": 0,
"label": "Reqd",
"oldfieldname": "reqd",
"label": "Hidden",
"oldfieldname": "hidden",
"oldfieldtype": "Check",
"permlevel": 0,
"print_hide": 0,
@ -112,12 +151,59 @@
"search_index": 0,
"width": "50px"
},
{
"fieldname": "column_break_14",
"fieldtype": "Column Break",
"permlevel": 0,
"precision": ""
},
{
"fieldname": "ignore_user_permissions",
"fieldtype": "Check",
"label": "Ignore User Permissions",
"permlevel": 0
},
{
"fieldname": "allow_on_submit",
"fieldtype": "Check",
"hidden": 0,
"label": "Allow on Submit",
"oldfieldname": "allow_on_submit",
"oldfieldtype": "Check",
"permlevel": 0,
"print_hide": 0,
"reqd": 0
},
{
"fieldname": "report_hide",
"fieldtype": "Check",
"hidden": 0,
"label": "Report Hide",
"oldfieldname": "report_hide",
"oldfieldtype": "Check",
"permlevel": 0,
"print_hide": 0,
"reqd": 0
},
{
"fieldname": "display",
"fieldtype": "Section Break",
"label": "Display",
"permlevel": 0,
"precision": ""
},
{
"fieldname": "default",
"fieldtype": "Text",
"hidden": 0,
"label": "Default",
"oldfieldname": "default",
"oldfieldtype": "Text",
"permlevel": 0,
"print_hide": 0,
"reqd": 0,
"search_index": 0
},
{
"fieldname": "in_filter",
"fieldtype": "Check",
@ -132,70 +218,10 @@
"width": "50px"
},
{
"fieldname": "in_list_view",
"fieldtype": "Check",
"label": "In List View",
"permlevel": 0
},
{
"fieldname": "hidden",
"fieldtype": "Check",
"hidden": 0,
"label": "Hidden",
"oldfieldname": "hidden",
"oldfieldtype": "Check",
"fieldname": "column_break_21",
"fieldtype": "Column Break",
"permlevel": 0,
"print_hide": 0,
"print_width": "50px",
"reqd": 0,
"search_index": 0,
"width": "50px"
},
{
"fieldname": "print_hide",
"fieldtype": "Check",
"hidden": 0,
"label": "Print Hide",
"oldfieldname": "print_hide",
"oldfieldtype": "Check",
"permlevel": 0,
"print_hide": 0,
"reqd": 0,
"search_index": 0
},
{
"fieldname": "report_hide",
"fieldtype": "Check",
"hidden": 0,
"label": "Report Hide",
"oldfieldname": "report_hide",
"oldfieldtype": "Check",
"permlevel": 0,
"print_hide": 0,
"reqd": 0
},
{
"fieldname": "allow_on_submit",
"fieldtype": "Check",
"hidden": 0,
"label": "Allow on Submit",
"oldfieldname": "allow_on_submit",
"oldfieldtype": "Check",
"permlevel": 0,
"print_hide": 0,
"reqd": 0
},
{
"description": "This field will appear only if the fieldname defined here has value OR the rules are true (examples): <br>\nmyfield\neval:doc.myfield=='My Value'<br>\neval:doc.age>18",
"fieldname": "depends_on",
"fieldtype": "Data",
"hidden": 0,
"label": "Depends On",
"oldfieldname": "depends_on",
"oldfieldtype": "Data",
"permlevel": 0,
"print_hide": 0,
"reqd": 0
"precision": ""
},
{
"fieldname": "description",
@ -211,16 +237,40 @@
"width": "300px"
},
{
"fieldname": "default",
"fieldtype": "Text",
"fieldname": "print_hide",
"fieldtype": "Check",
"hidden": 0,
"label": "Default",
"oldfieldname": "default",
"oldfieldtype": "Text",
"label": "Print Hide",
"oldfieldname": "print_hide",
"oldfieldtype": "Check",
"permlevel": 0,
"print_hide": 0,
"reqd": 0,
"search_index": 0
},
{
"description": "Print Width of the field, if the field is a column in a table",
"fieldname": "print_width",
"fieldtype": "Data",
"label": "Print Width",
"permlevel": 0,
"print_width": "50px",
"width": "50px"
},
{
"fieldname": "width",
"fieldtype": "Data",
"hidden": 0,
"in_list_view": 1,
"label": "Width",
"oldfieldname": "width",
"oldfieldtype": "Data",
"permlevel": 0,
"print_hide": 0,
"print_width": "50px",
"reqd": 0,
"search_index": 0,
"width": "50px"
}
],
"hide_heading": 0,
@ -228,7 +278,7 @@
"idx": 1,
"issingle": 0,
"istable": 1,
"modified": "2014-09-05 03:47:29.743676",
"modified": "2014-09-05 07:41:29.641454",
"modified_by": "Administrator",
"module": "Core",
"name": "Customize Form Field",

View file

@ -91,6 +91,16 @@
"fieldtype": "Column Break",
"permlevel": 0
},
{
"depends_on": "eval:in_list([\"Float\", \"Currency\", \"Percent\"], doc.fieldtype)",
"description": "Set non-standard precision for a Float or Currency field",
"fieldname": "precision",
"fieldtype": "Select",
"label": "Precision",
"options": "\n1\n2\n3\n4\n5\n6",
"permlevel": 0,
"print_hide": 1
},
{
"description": "For Links, enter the DocType as range\nFor Select, enter list of Options separated by comma",
"fieldname": "options",
@ -304,7 +314,7 @@
"in_dialog": 1,
"issingle": 0,
"istable": 1,
"modified": "2014-08-11 07:00:52.537012",
"modified": "2014-09-05 07:41:05.956027",
"modified_by": "Administrator",
"module": "Core",
"name": "DocField",

View file

@ -232,6 +232,10 @@ def validate_fields(fields):
if d.fieldtype == "Check" and d.default and d.default not in ('0', '1'):
frappe.throw(_("Default for 'Check' type of field must be either '0' or '1'"))
def check_precision(d):
if d.fieldtype in ("Currency", "Float", "Percent") and d.precision is not None and not (1 <= cint(d.precision) <= 6):
frappe.throw(_("Precision should be between 1 and 6"))
def check_fold(fields):
fold_exists = False
for i, f in enumerate(fields):

View file

@ -552,26 +552,24 @@ class Document(BaseDocument):
if parentfield and not isinstance(parentfield, basestring):
parentfield = parentfield.parentfield
cache_key = parentfield or "main"
if not hasattr(self, "_precision"):
self._precision = frappe._dict({
"default": cint(frappe.db.get_default("float_precision")) or 3,
"options": {}
})
self._precision = frappe._dict()
if self._precision.setdefault(parentfield or "main", {}).get(fieldname) is None:
meta = frappe.get_meta(self.meta.get_field(parentfield).options if parentfield else self.doctype)
df = meta.get_field(fieldname)
if cache_key not in self._precision:
self._precision[cache_key] = frappe._dict()
if df.fieldtype == "Currency" and df.options and not self._precision.options.get(df.options):
self._precision.options[df.options] = get_field_precision(df, self)
if fieldname not in self._precision[cache_key]:
self._precision[cache_key][fieldname] = None
if df.fieldtype == "Currency":
self._precision[parentfield or "main"][fieldname] = cint(self._precision.options.get(df.options)) or \
self._precision.default
elif df.fieldtype in ("Float", "Percent"):
self._precision[parentfield or "main"][fieldname] = self._precision.default
doctype = self.meta.get_field(parentfield).options if parentfield else self.doctype
df = frappe.get_meta(doctype).get_field(fieldname)
return self._precision[parentfield or "main"][fieldname]
if df.fieldtype in ("Currency", "Float", "Percent"):
self._precision[cache_key][fieldname] = get_field_precision(df, self)
return self._precision[cache_key][fieldname]
def get_url(self):
return "/desk#Form/{doctype}/{name}".format(doctype=self.doctype, name=self.name)

View file

@ -258,19 +258,23 @@ def get_field_precision(df, doc):
"""get precision based on DocField options and fieldvalue in doc"""
from frappe.utils import get_number_format_info
number_format = None
precision = cint(df.precision) or cint(frappe.db.get_default("float_precision")) or 3
if df.fieldtype == "Currency":
number_format = None
currency = get_field_currency(df, doc)
if not currency:
# use default currency
currency = frappe.db.get_default("currency")
if currency:
number_format = frappe.db.get_value("Currency", currency, "number_format")
if not number_format:
number_format = frappe.db.get_default("number_format") or "#,###.##"
if not number_format:
number_format = frappe.db.get_default("number_format") or "#,###.##"
decimal_str, comma_str, precision = get_number_format_info(number_format)
if df.fieldtype in ("Float", "Percent"):
precision = cint(frappe.db.get_default("float_precision")) or 3
decimal_str, comma_str, precision = get_number_format_info(number_format)
return precision

View file

@ -385,20 +385,26 @@ frappe.ui.form.ControlFloat = frappe.ui.form.ControlInt.extend({
return callback(isNaN(parseFloat(value)) ? null : flt(value));
},
format_for_input: function(value) {
var formatted_value = format_number(parseFloat(value),
null, cint(frappe.boot.sysdefaults.float_precision, null));
var number_format;
var precision = this.df.precision || cint(frappe.boot.sysdefaults.float_precision, null);
if (this.df.fieldtype==="Float" && this.df.options && this.df.options.trim()) {
number_format = get_number_format(this.get_currency());
}
var formatted_value = format_number(parseFloat(value), number_format, precision);
return isNaN(parseFloat(value)) ? "" : formatted_value;
},
// even a float field can be formatted based on currency format instead of float format
get_currency: function() {
return frappe.meta.get_field_currency(this.df, this.get_doc());
}
});
frappe.ui.form.ControlCurrency = frappe.ui.form.ControlFloat.extend({
format_for_input: function(value) {
var formatted_value = format_number(parseFloat(value),
get_number_format(this.get_currency()));
get_number_format(this.get_currency()), this.df.precision || null);
return isNaN(parseFloat(value)) ? "" : formatted_value;
},
get_currency: function() {
return frappe.meta.get_field_currency(this.df, this.get_doc());
}
});

View file

@ -19,12 +19,28 @@ frappe.form.formatters = {
Select: function(value) {
return __(frappe.form.formatters["Data"](value));
},
Float: function(value, docfield, options) {
var decimals = cint(docfield.options, null) || cint(frappe.boot.sysdefaults.float_precision, null);
return frappe.form.formatters._right(
((value==null || value==="")
? ""
: format_number(value, null, decimals)), options)
Float: function(value, docfield, options, doc) {
// don't allow 0 precision for Floats, hence or'ing with null
var precision = docfield.precision || cint(frappe.boot.sysdefaults.float_precision) || null;
if (docfield.options && docfield.options.trim()) {
// options points to a currency field, but expects precision of float!
docfield.precision = precision;
return frappe.form.formatters.Currency(value, docfield, options, doc);
} else {
// show 1.000000 as 1
if (!is_null(value)) {
var temp = cstr(value).split(".");
if (temp[1]==undefined || cint(temp[1])===0) {
precision = 0;
}
}
return frappe.form.formatters._right(
((value==null || value==="")
? ""
: format_number(value, null, precision)), options);
}
},
Int: function(value, docfield, options) {
return frappe.form.formatters._right(value==null ? "" : cint(value), options)
@ -35,7 +51,7 @@ frappe.form.formatters = {
Currency: function(value, docfield, options, doc) {
var currency = frappe.meta.get_field_currency(docfield, doc);
return frappe.form.formatters._right((value==null || value==="")
? "" : format_currency(value, currency), options);
? "" : format_currency(value, currency, docfield.precision || null), options);
},
Check: function(value) {
return value ? "<i class='icon-check'></i>" : "<i class='icon-check-empty'></i>";

View file

@ -562,6 +562,7 @@ frappe.ui.form.GridRow = Class.extend({
// in form
if(this.fields_dict && this.fields_dict[fieldname]) {
this.fields_dict[fieldname].refresh();
this.layout.refresh_dependency();
}
},
get_visible_columns: function(blacklist) {

View file

@ -71,6 +71,9 @@ frappe.ui.form.Layout = Class.extend({
.appendTo(me.wrapper)
}
}, 100);
// dependent fields
this.refresh_dependency();
},
render: function() {
var me = this;
@ -318,5 +321,59 @@ frappe.ui.form.Layout = Class.extend({
get_open_grid_row: function() {
return $(".grid-row-open").data("grid_row");
},
refresh_dependency: function() {
// Resolve "depends_on" and show / hide accordingly
var me = this;
var doc = me.doc;
if (!doc) return;
var parent = doc.parent ? locals[doc.parenttype][doc.parent] : {};
// build dependants' dictionary
var has_dep = false;
for(fkey in this.fields_list) {
var f = this.fields_list[fkey];
f.dependencies_clear = true;
if(f.df.depends_on) {
has_dep = true;
}
}
if(!has_dep)return;
// show / hide based on values
for(var i=me.fields_list.length-1;i>=0;i--) {
var f = me.fields_list[i];
f.guardian_has_value = true;
if(f.df.depends_on) {
// evaluate guardian
if(f.df.depends_on.substr(0,5)=='eval:') {
f.guardian_has_value = eval(f.df.depends_on.substr(5));
} else if(f.df.depends_on.substr(0,3)=='fn:' && me.frm) {
f.guardian_has_value = me.frm.script_manager.trigger(f.df.depends_on.substr(3), me.doctype, me.docname);
} else {
if(!doc[f.df.depends_on]) {
f.guardian_has_value = false;
}
}
// show / hide
if(f.guardian_has_value) {
if(f.df.hidden_due_to_dependency) {
f.df.hidden_due_to_dependency = false;
f.refresh();
}
} else {
if(!f.df.hidden_due_to_dependency) {
f.df.hidden_due_to_dependency = true;
f.refresh();
}
}
}
}
this.refresh_section_count();
}
})

View file

@ -76,8 +76,8 @@ window.format_number = function(v, format, decimals){
info = get_number_format_info(format);
//Fix the decimal first, toFixed will auto fill trailing zero.
decimals = decimals || info.precision;
// Fix the decimal first, toFixed will auto fill trailing zero.
if (decimals == null) decimals = info.precision;
v = flt(v, decimals, format);

View file

@ -168,12 +168,14 @@ $.extend(frappe.meta, {
},
get_field_precision: function(df, doc) {
var precision = frappe.defaults.get_default("float_precision") || 3;
if(df && df.fieldtype === "Currency") {
var precision = cint(frappe.defaults.get_default("float_precision")) || 3;
if (df && cint(df.precision)) {
precision = cint(df.precision);
} else if(df && df.fieldtype === "Currency") {
var currency = this.get_field_currency(df, doc);
var number_format = get_number_format(currency);
var number_format_info = get_number_format_info(number_format);
precision = number_format_info.precision || precision;
precision = number_format_info.precision;
}
return precision;
},

View file

@ -194,7 +194,7 @@ _f.Frm.prototype.watch_model_updates = function() {
me.fields_dict[fieldname]
&& me.fields_dict[fieldname].refresh(fieldname);
me.refresh_dependency();
me.layout.refresh_dependency();
me.script_manager.trigger(fieldname, doc.doctype, doc.name);
}
})
@ -432,9 +432,6 @@ _f.Frm.prototype.refresh_fields = function() {
// cleanup activities after refresh
this.cleanup_refresh(this);
// dependent fields
this.refresh_dependency();
}
@ -468,60 +465,6 @@ _f.Frm.prototype.cleanup_refresh = function() {
}
}
// Resolve "depends_on" and show / hide accordingly
_f.Frm.prototype.refresh_dependency = function() {
var me = this;
var doc = locals[this.doctype][this.docname];
// build dependants' dictionary
var has_dep = false;
for(fkey in me.fields) {
var f = me.fields[fkey];
f.dependencies_clear = true;
if(f.df.depends_on) {
has_dep = true;
}
}
if(!has_dep)return;
// show / hide based on values
for(var i=me.fields.length-1;i>=0;i--) {
var f = me.fields[i];
f.guardian_has_value = true;
if(f.df.depends_on) {
// evaluate guardian
var v = doc[f.df.depends_on];
if(f.df.depends_on.substr(0,5)=='eval:') {
f.guardian_has_value = eval(f.df.depends_on.substr(5));
} else if(f.df.depends_on.substr(0,3)=='fn:') {
f.guardian_has_value = me.script_manager.trigger(f.df.depends_on.substr(3), me.doctype, me.docname);
} else {
if(!v) {
f.guardian_has_value = false;
}
}
// show / hide
if(f.guardian_has_value) {
if(f.df.hidden_due_to_dependency) {
f.df.hidden_due_to_dependency = false;
f.refresh();
}
} else {
if(!f.df.hidden_due_to_dependency) {
f.df.hidden_due_to_dependency = true;
f.refresh();
}
}
}
}
this.layout.refresh_section_count();
}
_f.Frm.prototype.setnewdoc = function() {
// moved this call to refresh function
// this.check_doctype_conflict(docname);

View file

@ -261,7 +261,7 @@ def fmt_money(amount, precision=None, currency=None):
decimal_str, comma_str, number_format_precision = get_number_format_info(number_format)
if not precision:
if precision is None:
precision = number_format_precision
amount = '%.*f' % (precision, flt(amount))

View file

@ -3,7 +3,7 @@
from __future__ import unicode_literals
import frappe
from frappe.utils import formatdate, fmt_money, flt
from frappe.utils import formatdate, fmt_money, flt, cstr, cint
from frappe.model.meta import get_field_currency, get_field_precision
import re
@ -11,12 +11,21 @@ def format_value(value, df, doc=None, currency=None):
if df.get("fieldtype")=="Date":
return formatdate(value)
elif df.get("fieldtype") == "Currency":
elif df.get("fieldtype") == "Currency" or (df.get("fieldtype")=="Float" and (df.options or "").strip()):
return fmt_money(value, precision=get_field_precision(df, doc),
currency=currency if currency else (get_field_currency(df, doc) if doc else None))
elif df.get("fieldtype") == "Float":
return fmt_money(value, precision=get_field_precision(df, doc))
precision = get_field_precision(df, doc)
# show 1.000000 as 1
# options should not specified
if not df.options and value is not None:
temp = cstr(value).split(".")
if len(temp)==1 or cint(temp[1])==0:
precision = 0
return fmt_money(value, precision=precision)
elif df.get("fieldtype") == "Percent":
return "{}%".format(flt(value, 2))