use decimal.Decimal instead of float to store amounts of money

This commit is contained in:
Romain Bignon 2012-03-29 16:14:32 +02:00
commit b157e92d5b
28 changed files with 111 additions and 69 deletions

View file

@ -18,6 +18,7 @@
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from decimal import Decimal
import sys
from weboob.capabilities.bank import ICapBank, Account, Transaction
@ -123,8 +124,8 @@ class AccountListFormatter(IFormatter):
MANDATORY_FIELDS = ('id', 'label', 'balance', 'coming')
count = 0
tot_balance = 0.0
tot_coming = 0.0
tot_balance = Decimal(0)
tot_coming = Decimal(0)
def flush(self):
if self.count < 1:
@ -134,8 +135,8 @@ class AccountListFormatter(IFormatter):
result += u'%s Total %8s %8s' % ((' ' * 15) if not self.interactive else '',
'%.2f' % self.tot_balance, '%.2f' % self.tot_coming)
self.after_format(result)
self.tot_balance = 0.0
self.tot_coming = 0.0
self.tot_balance = Decimal(0)
self.tot_coming = Decimal(0)
self.count = 0
def format_dict(self, item):
@ -152,7 +153,7 @@ class AccountListFormatter(IFormatter):
result += '------------------------------------------%s+----------+----------\n' % (('-' * 15) if not self.interactive else '')
result += (u' %s%-' + (u'15' if self.interactive else '30') + u's%s %-25s %8s %8s') % \
(self.BOLD, id, self.NC,
item['label'], '%.2f' % item['balance'], '%.2f' % (item['coming'] or 0.0))
item['label'], '%.2f' % item['balance'], '%.2f' % (item['coming'] or Decimal(0.0)))
self.tot_balance += item['balance']
if item['coming']:
@ -281,7 +282,7 @@ class Boobank(ReplApplication):
id_to, backend_name_to = self.parse_id(id_to)
try:
amount = float(amount)
amount = Decimal(amount)
except (TypeError, ValueError):
print >>sys.stderr, 'Error: please give a decimal amount to transfer'
return 2

View file

@ -20,6 +20,8 @@
from PyQt4.QtGui import QListWidgetItem, QImage, QPixmap, QLabel, QIcon, QBrush, QColor
from PyQt4.QtCore import SIGNAL, Qt
from decimal import Decimal
from weboob.tools.application.qt import QtMainWindow, QtDo, HTMLDelegate
from weboob.tools.application.qt.backendcfg import BackendCfg
from weboob.capabilities.housing import ICapHousing, Query, City
@ -35,8 +37,8 @@ class HousingListWidgetItem(QListWidgetItem):
self.read = True
def __lt__(self, other):
return '%s%s' % (self.read, float(self.housing.cost or 0) / float(self.housing.area or 1)) < \
'%s%s' % (other.read, float(other.housing.cost or 0) / float(other.housing.area or 1))
return '%s%s' % (self.read, Decimal(self.housing.cost or 0) / Decimal(self.housing.area or 1)) < \
'%s%s' % (other.read, Decimal(other.housing.cost or 0) / Decimal(other.housing.area or 1))
def setAttrs(self, storage):
text = u'<h2>%s</h2>' % self.housing.title

View file

@ -20,7 +20,7 @@
from datetime import date, datetime
from .base import CapBaseObject, Field, StringField, DateField, FloatField, IntField
from .base import CapBaseObject, Field, StringField, DateField, DecimalField, IntField
from .collection import ICapCollection
@ -66,8 +66,8 @@ class Account(Recipient):
TYPE_JOINT = 6 # Joint account
type = IntField('Type of account', default=TYPE_UNKNOWN)
balance = FloatField('Balance on this bank account')
coming = FloatField('Coming balance')
balance = DecimalField('Balance on this bank account')
coming = DecimalField('Coming balance')
def __repr__(self):
return u"<Account id=%r label=%r>" % (self.id, self.label)
@ -94,7 +94,7 @@ class Transaction(CapBaseObject):
raw = StringField('Raw label of the transaction')
category = StringField('Category of transaction')
label = StringField('Pretty label')
amount = FloatField('Amount of transaction')
amount = DecimalField('Amount of transaction')
def __repr__(self):
return "<Transaction date='%s' label='%s' amount=%s>" % (self.date,
@ -105,7 +105,7 @@ class Transfer(CapBaseObject):
Transfer from an account to a recipient.
"""
amount = FloatField('Amount to transfer')
amount = DecimalField('Amount to transfer')
date = Field('Date of transfer', basestring, date, datetime)
origin = Field('Origin of transfer', int, long, basestring)
recipient = Field('Recipient', int, long, basestring)
@ -193,7 +193,7 @@ class ICapBank(ICapCollection):
:param recipient: account to send money
:type recipient: :class:`Recipient`
:param amount: amount
:type amount: :class:`float`
:type amount: :class:`decimal.Decimal`
:param reason: reason of transfer
:type reason: :class:`unicode`
:rtype: :class:`Transfer`

View file

@ -20,6 +20,7 @@
import warnings
import datetime
from decimal import Decimal
from copy import deepcopy
from weboob.tools.misc import to_unicode
@ -27,8 +28,8 @@ from weboob.tools.ordereddict import OrderedDict
__all__ = ['FieldNotFound', 'NotAvailable', 'NotLoaded', 'IBaseCap',
'Field', 'IntField', 'FloatField', 'StringField', 'BytesField',
'DateField', 'DeltaField', 'CapBaseObject', 'empty']
'Field', 'IntField', 'DecimalField', 'FloatField', 'StringField',
'BytesField', 'DateField', 'DeltaField', 'CapBaseObject', 'empty']
def empty(value):
@ -154,6 +155,18 @@ class IntField(Field):
def convert(self, value):
return int(value)
class DecimalField(Field):
"""
A field which accepts only :class:`decimal` type.
"""
def __init__(self, doc, **kwargs):
Field.__init__(self, doc, Decimal, **kwargs)
def convert(self, value):
if isinstance(value, Decimal):
return value
return Decimal(value)
class FloatField(Field):
"""
A field which accepts only :class:`float` type.
@ -244,7 +257,7 @@ class CapBaseObject(object):
class Transfer(CapBaseObject):
" Transfer from an account to a recipient. "
amount = FloatField('Amount to transfer')
amount = DecimalField('Amount to transfer')
date = Field('Date of transfer', basestring, date, datetime)
origin = Field('Origin of transfer', int, long, basestring)
recipient = Field('Recipient', int, long, basestring)

View file

@ -17,7 +17,7 @@
# You should have received a copy of the GNU Affero General Public License
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from .base import CapBaseObject, StringField, DateField, FloatField
from .base import CapBaseObject, StringField, DateField, DecimalField
from .collection import ICapCollection
@ -47,7 +47,7 @@ class Detail(CapBaseObject):
label = StringField('label of the detail line')
infos = StringField('information')
datetime = DateField('date information')
price = FloatField('price')
price = DecimalField('price')
def __init__(self):
CapBaseObject.__init__(self, 0)

View file

@ -18,7 +18,7 @@
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from .base import IBaseCap, CapBaseObject, Field, IntField, FloatField, \
from .base import IBaseCap, CapBaseObject, Field, IntField, DecimalField, \
StringField, BytesField, DateField
@ -50,8 +50,8 @@ class Housing(CapBaseObject):
Content of a housing.
"""
title = StringField('Title of housing')
area = FloatField('Area of housing, in m2')
cost = FloatField('Cost of housing')
area = DecimalField('Area of housing, in m2')
cost = DecimalField('Cost of housing')
currency = StringField('Currency of cost')
date = DateField('Date when the housing has been published')
location = StringField('Location of housing')

View file

@ -18,7 +18,7 @@
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from .base import IBaseCap, CapBaseObject, Field, FloatField, \
from .base import IBaseCap, CapBaseObject, Field, DecimalField, \
StringField, DateField
@ -44,7 +44,7 @@ class Price(CapBaseObject):
Price.
"""
date = DateField('Date when this price has been published')
cost = FloatField('Cost of the product in this shop')
cost = DecimalField('Cost of the product in this shop')
currency = StringField('Currency of the price')
message = StringField('Message related to this price')
shop = Field('Shop information', Shop)

View file

@ -18,6 +18,7 @@
# along with weboob. If not, see <http://www.gnu.org/licenses/>.
from decimal import Decimal
import re
import datetime
@ -53,9 +54,9 @@ class FrenchTransaction(Transaction):
debit = self.clean_amount(debit)
if len(debit) > 0:
self.amount = - float(debit)
self.amount = - Decimal(debit)
else:
self.amount = float(credit)
self.amount = Decimal(credit)
def parse(self, date, raw):
"""

View file

@ -21,6 +21,7 @@
from __future__ import with_statement
from ConfigParser import RawConfigParser, DEFAULTSECT
from decimal import Decimal
import logging
import os
@ -70,7 +71,7 @@ class INIConfig(IConfig):
def save(self):
def save_section(values, root_section=self.ROOTSECT):
for k, v in values.iteritems():
if isinstance(v, (int, float, basestring)):
if isinstance(v, (int, Decimal, float, basestring)):
if not self.config.has_section(root_section):
self.config.add_section(root_section)
self.config.set(root_section, k, unicode(v))