Skip to content

Instantly share code, notes, and snippets.

@MaxMorais
Last active November 10, 2017 19:33
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MaxMorais/5013885 to your computer and use it in GitHub Desktop.
Save MaxMorais/5013885 to your computer and use it in GitHub Desktop.
Web2py MoneyInput Widget
# This widget requires jQuery-iMask to run
#https://github.com/cwolves/jQuery-iMask
def money_widget_factory(international=False):
import locale, string, random
from gluon.sqlhtml import StringWidget
from gluon import current
from gluon.http import HTTP
T = current.T
locale.setlocale(locale.LC_ALL, '')
loc = locale.localeconv()
if not getattr(current.session, 'mw_uids', None):
current.session.mw_uids = {}
if not international:
SYMBOL = loc.get('currency_symbol', None) or ''
DECIMAL_PLACES = loc.get('frac_digits', None) or 2
DECIMAL_SEPARATOR = loc.get('mon_decimal_point', None) or '.'
THOUSAND_PLACES = loc.get('mon_grouping', [3,0])[0]
THOUSAND_SEPARATOR = loc.get('mon_thousands_sep', None) or ','
else:
SYMBOL = loc.get('int_curr_symbol', None) or '',
DECIMAL_PLACES = loc.get('int_frac_digits', None) or 2
DECIMAL_SEPARATOR = loc.get('mon_decimal_point', None) or '.'
THOUSAND_PLACES = loc.get('mon_grouping', [3,0])[0]
THOUSAND_SEPARATOR = loc.get('mon_thousands_sep', None) or ','
lib = URL(c='static', f='js/jquery-imask.js');
basescripts = ["""
;/* Plugin jQuery para o agendamento da execucao de uma determinada acao
no instante em que o objeto passar a existir */
$.fn.onAvailable = function(fn){
var self = this, sel = this.selector, timer;
if (this.length > 0) {
fn.call(this);
} else {
timer = setInterval(function(){
if ($(sel).length > 0) {
fn.call(self);
clearInterval(timer);
}
}, 50);
}
};""",
"""
;function maskMoneyThis(uid){
$('input[uid="' + uid + '"]').onAvailable(function(){
var element = $('input[uid="' + uid + '"]');
if (!element.data('masked')){
element.iMask({
'type': 'number',
'currencySymbol': element.data('symbol'),
'decDigits': parseFloat(element.data('decimal_places')),
'groupSymbol': element.data('thousand_separator'),
'decSymbol': element.data('decimal_separator'),
'groupDigits': parseFloat(element.data('thousand_places'))
});
element.focus();
}
element.focusout(function(event){
var val = element.val()
.replace(element.data('symbol'), '')
.replace(element.data('thousand_separator'), '')
.replace(element.data('decimal_separator'), '.');
if (val.length){
console.log(element.data('url')+'?'+element.data('keyword')+'='+val);
console.log(element.data('target'));
ajax(element.data('url')+'?'+element.data('keyword')+'='+val, [], element.data('target'));
}
});
});
};
"""
]
if not lib in response.files:
response.files.append(lib)
response.js = (response.js or '')
for basescript in basescripts:
if not basescript in response.js:
response.js += basescript.strip()
def in_words(integer):
"""
Retorna string por extenso para o dado inteiro.
"""
if not integer: return ''
n = int(integer)
known = {
0: T('zero'), 1: T('one'), 2: T('two'), 3: T('three'), 4: T('four'),
5: T('five'), 6: T('six'), 7: T('seven'), 8: T('eight'), 9: T('nine'),
10: T('ten'), 11: T('eleven'), 12 : T('twelve'), 13: T('thirteen'), 14: T('fourteen'),
15: T('fifteen'), 16: T('sixteen'), 17: T('seventeen'), 18: T('eighteen'), 19: T('nineteen'),
20: T('twenty'), 30: T('thirty'), 40: T('forty'), 50: T('fifty'), 60: T('sixty'),
70: T('seventy'), 80: T('eighty'), 90: T('ninety')
}
def psn(n, known, xpsn):
import sys;
if n in known: return known[n]
bestguess, remainder = str(n), 0
if n<=20:
print >> sys.stderr, n, T('How did this happen?')
assert 0
elif n < 100:
bestguess = xpsn((n//10)*10, known, xpsn) + '-' + xpsn(n%10, known, xpsn)
return bestguess
elif n < 1000:
bestguess = xpsn(n//100, known, xpsn) + ' ' + T('hundred')
remainder = n % 100
elif n < 1000000:
bestguess= xpsn(n//1000, known, xpsn) + ' ' + T('thousand')
remainder = n % 1000
elif n < 1000000000:
bestguess= xpsn(n//1000000, known, xpsn) + ' ' + T('million')
remainder = n%1000000
else:
bestguess= xpsn(n//1000000000, known, xpsn) + ' ' + T('billion')
remainder = n%1000000000
if remainder:
comma = ',' if remainder >= 100 else ''
return bestguess + comma + ' ' + xpsn(remainder, known, xpsn)
else:
return bestguess
return psn(n, known, psn)
def money_in_words(value, main_currency='reais', fraction_currency='centavos'):
"""
Retorna uma string por extenso com a moeda e a fracao de moeda
"""
n = "%0.2f" % float(value)
main, fraction = n.split('.')
if len(fraction) == 1:
fraction += '0'
out = in_words(main).title() + ' ' + main_currency
if int(fraction):
out = out + ' ' + T('and') + ' ' + in_words(fraction).title() + ' ' + fraction_currency
return out + '.'
# Cria uma string randomica - útil para identificadores
UID = lambda length=8: ''.join([random.choice(string.ascii_letters+string.digits) for x in range(length)])
class MoneyWidget(StringWidget):
_class = 'money'
_keyword = 'keyword_'
@classmethod
def widget(cls, field, value, **attributes):
#remove todos os caracters que não compõem um decimal
fin = lambda data: str(data).replace(SYMBOL, '').replace(THOUSAND_SEPARATOR, '').replace(DECIMAL_SEPARATOR, '.').strip() or '0.00' if data else None
if request.vars:
if request.vars[field.name]:
request.vars[field.name] = fin(request.vars[field.name])
uid = current.session.mw_uids[field.name]
keyword = cls._keyword+uid
if keyword in request.vars:
cls.callback(uid)
else:
uid = UID()
current.session.mw_uids[field.name] = uid
keyword = cls._keyword+uid
target = field.name + '_extenso'
defaults = {
'_data-SYMBOL': (SYMBOL + ' ') if SYMBOL else '',
'_data-DECIMAL_PLACES': DECIMAL_PLACES,
'_data-DECIMAL_SEPARATOR': DECIMAL_SEPARATOR,
'_data-THOUSAND_PLACES': THOUSAND_PLACES,
'_data-THOUSAND_SEPARATOR': THOUSAND_SEPARATOR,
'_data-URL': URL(),
'_data-KEYWORD': keyword,
'_data-TARGET': target,
'_uid': uid,
}
attr = cls._attributes(field, defaults, **attributes)
script = ";maskMoneyThis(%s);"%`uid`
if not script in response.js:
response.js += script
return DIV(
StringWidget.widget(field, fin(value), **attr),
P(_id=target)
)
@classmethod
def callback(cls, uid):
value = request.vars[cls._keyword+uid]
raise HTTP(200, money_in_words(value))
return MoneyWidget.widget
Contatos = db.define_table('contatos',
Field('nome', 'string', notnull=True),
Field('email', 'string'),
Field('celular', 'string', widget=masked_widget_factory('(99) 9999-9999')),
Field('emprestimo', 'float', widget=money_widget_factory()),
)

TODO

  • Suporte a extenso
  • Geração Automática (param: bol:show_in_words )
  • Possibilidade de armazenamento em outro campo (param: Field:in_words_field)
  • Suporte a multiplas moedas
  • Armazenamento da moeda
  • Suporte a internacionalização baseado nas informações do unicode.org
  • Tendo em vista que as informações de formatos de moeda, estão disponíveis e parametrizadas na base de dados do unicode.org, é interessante utilizar estas informações, pois com base nas configurações, ou moeda escolhida, pode-se formatar a visualização exata de cada moeda.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment