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",
42 'nonzero': "Please enter a non-zero amount"}
44 def _to_python(self, value, state):
47 except InvalidOperation:
48 raise Invalid(self.message('amount', state),
51 ret = dec.quantize(Decimal('1.00'))
53 raise Invalid(self.message('nonzero', state),
56 raise Invalid(self.message('precision', state),
59 return Currency(int(ret * 100))
62 class Currency(object):
64 Store currency values as an integral number of cents
66 __metaclass__ = SmartSubclass(int)
67 __old_values__ = WeakValueDictionary()
68 def __new__(cls, value):
71 elif isinstance(value, str):
72 value = int(float(value) * 100)
76 if value not in cls.__old_values__:
77 new_object = super(cls, cls).__new__(cls)
78 new_object.value = value
79 cls.__old_values__[value] = new_object
82 return cls.__old_values__[value]
86 If I don't define this, SmartSubclass will return
87 Currency(int(self.value))
92 If I don't define this, SmartSubclass will return
93 Currency(float(self.value))
95 return float(self.value)
98 If I don't define this, SmartSubclass will return
99 Currency(long(self.value))
101 return long(self.value)
103 def __cmp__(self, other):
105 This is overridden for when validators compare a Currency to
111 return self.value.__cmp__(int(other))
113 def __mul__(self, other):
115 If I don't define this, SmartSubclass will convert the other
118 return Currency(self.value * other)
119 def __rmul__(self, other):
121 If I don't define this, SmartSubclass will convert the other
124 return self.__mul__(other)
127 return '%s("%s")' % (self.__class__.__name__, str(self))
129 return locale.currency(self.value / 100., grouping=True)
132 class DBCurrency(sa.types.TypeDecorator):
134 A type which represents monetary amounts internally as integers.
136 This avoids binary/decimal float conversion issues
139 impl = sa.types.Integer
141 def process_bind_param(self, value, engine):
144 def convert_result_value(self, value, engine):
145 return Currency(value)
146 process_result_value = convert_result_value