2 Define special types used in BlueChips
6 from decimal import Decimal, InvalidOperation
8 import sqlalchemy as sa
9 from formencode import validators, Invalid
10 from bluechips.lib.subclass import SmartSubclass
12 from weakref import WeakValueDictionary
15 "Manually install en_US for systems that don't have it."
16 d = {'currency_symbol': '$',
19 'grouping': [3, 3, 0],
20 'int_curr_symbol': 'USD ',
22 'mon_decimal_point': '.',
23 'mon_grouping': [3, 3, 0],
24 'mon_thousands_sep': ',',
35 locale.localeconv = localeconv
38 class CurrencyValidator(validators.FancyValidator):
39 "A validator to convert to Currency objects."
40 messages = {'amount': "Please enter a valid currency amount",
41 'precision': "Only two digits after the decimal, please"}
43 def _to_python(self, value, state):
46 except InvalidOperation:
47 raise Invalid(self.message('amount', state),
50 ret = dec.quantize(Decimal('1.00'))
52 raise Invalid(self.message('precision', state),
55 return Currency(int(ret * 100))
58 class Currency(object):
60 Store currency values as an integral number of cents
62 __metaclass__ = SmartSubclass(int)
63 __old_values__ = WeakValueDictionary()
64 def __new__(cls, value):
67 elif isinstance(value, str):
68 value = int(float(value) * 100)
72 if value not in cls.__old_values__:
73 new_object = super(cls, cls).__new__(cls)
74 new_object.value = value
75 cls.__old_values__[value] = new_object
78 return cls.__old_values__[value]
82 If I don't define this, SmartSubclass will return
83 Currency(int(self.value))
88 If I don't define this, SmartSubclass will return
89 Currency(float(self.value))
91 return float(self.value)
94 If I don't define this, SmartSubclass will return
95 Currency(long(self.value))
97 return long(self.value)
99 def __cmp__(self, other):
101 This is overridden for when validators compare a Currency to
107 return self.value.__cmp__(int(other))
109 def __mul__(self, other):
111 If I don't define this, SmartSubclass will convert the other
114 return Currency(self.value * other)
115 def __rmul__(self, other):
117 If I don't define this, SmartSubclass will convert the other
120 return self.__mul__(other)
122 def __str_no_dollar__(self):
124 Get to the formatted string without the dollar sign
126 return str(self).replace('$', '')
129 return '%s("%s")' % (self.__class__.__name__, str(self))
131 return locale.currency(self.value / 100., grouping=True)
134 class DBCurrency(sa.types.TypeDecorator):
136 A type which represents monetary amounts internally as integers.
138 This avoids binary/decimal float conversion issues
141 impl = sa.types.Integer
143 def process_bind_param(self, value, engine):
146 def convert_result_value(self, value, engine):
147 return Currency(value)
148 process_result_value = convert_result_value