fsb: Handle some bankid failures better.
[fulbank.git] / fulbank / currency.py
1 class currency(object):
2     def __init__(self, symbol):
3         self.symbol = symbol
4
5     def sformat(self, amount):
6         return "%s %s" % (self.symbol, self.format(amount))
7
8     def __repr__(self):
9         return "#<currency %s>" % self.symbol
10
11     @property
12     def zero(self):
13         return value(0, self)
14
15     _known = {}
16     @classmethod
17     def define(cls, *args, **kwargs):
18         self = cls(*args, **kwargs)
19         cls._known[self.symbol] = self
20     @classmethod
21     def get(cls, symbol):
22         return cls._known[symbol]
23
24     def __reduce__(self):
25         return _currency_restore, (type(self), self.symbol,)
26 def _currency_restore(cls, symbol):
27     return cls.get(symbol)
28
29 class integral(currency):
30     def __init__(self, symbol):
31         super().__init__(symbol)
32
33     def format(self, amount):
34         return "%i" % amount
35
36     def parse(self, text):
37         return value(int(text), self)
38
39 class decimal(currency):
40     def __init__(self, symbol, separator, thseparator, decimals=2):
41         super().__init__(symbol)
42         self.separator = separator
43         self.thseparator = thseparator
44         self.decimals = decimals
45
46     def format(self, amount):
47         if amount < 0:
48             return "-" + self.format(-amount)
49         bias = 10 ** self.decimals
50         ip = amount // bias
51         fp = amount - (ip * bias)
52         return "%i.%0*i" % (ip, self.decimals, fp)
53
54     def parse(self, text):
55         def parse2(text):
56             p = text.find(self.separator)
57             bias = 10 ** self.decimals
58             if p < 0:
59                 text = text.replace(self.thseparator)
60                 if not text.isdigit():
61                     raise ValueError(text)
62                 return int(text) * bias
63             else:
64                 if p != len(text) - 3:
65                     raise ValueError(text)
66                 ip = text[:p].replace(self.thseparator, "")
67                 fp = text[p + 1:]
68                 if not (ip.isdigit() and fp.isdigit()):
69                     raise ValueError(text)
70                 ip = int(ip)
71                 fp = int(fp)
72                 if fp >= bias:
73                     raise ValueError(text)
74                 return (ip * bias) + fp
75         if text[0:1] == "-":
76             return value(-parse2(text[1:]), self)
77         else:
78             return value(parse2(text), self)
79
80 decimal.define("SEK", ",", " ")
81 decimal.define("USD", ".", ",")
82 integral.define("JPY")
83
84 class value(object):
85     __slots__ = ["amount", "currency"]
86     def __init__(self, amount, currency):
87         self.amount = int(amount)
88         self.currency = currency
89
90     def __repr__(self):
91         return "%s %s" % (self.currency.symbol, self.currency.format(self.amount))
92
93     def __add__(self, other):
94         if self.currency != other.currency:
95             raise ValueError("cannot add %s to %s" % (other.currency.symbol, self.currency.symbol))
96         return value(int(self.amount + other.amount), self.currency)
97     def __sub__(self, other):
98         if self.currency != other.currency:
99             raise ValueError("cannot subtract %s from %s" % (other.currency.symbol, self.currency.symbol))
100         return value(int(self.amount - other.amount), self.currency)
101     def __mul__(self, other):
102         return value(int(self.amount * other), self.currency)
103     def __truediv__(self, other):
104         if self.currency != other.currency:
105             raise ValueError("cannot divide %s with %s" % (self.currency.symbol, other.currency.symbol))
106         return self.amount / other.amount
107     def __floordiv__(self, other):
108         if self.currency != other.currency:
109             raise ValueError("cannot divide %s with %s" % (self.currency.symbol, other.currency.symbol))
110         return self.amount // other.amount
111     def __mod__(self, other):
112         if self.currency != other.currency:
113             raise ValueError("cannot divide %s with %s" % (self.currency.symbol, other.currency.symbol))
114         return value(int(self.amount % other.amount), self.currency)
115     def __divmod__(self, other):
116         if self.currency != other.currency:
117             raise ValueError("cannot divide %s with %s" % (self.currency.symbol, other.currency.symbol))
118         return (self.amount // other.amount, value(int(self.amount % other.amount), self.currency))
119     def __neg__(self):
120         return value(-self.amount, self.currency)
121
122     def __eq__(self, other):
123         if self.currency != other.currency:
124             raise ValueError("cannot compare %s with %s" % (self.currency.symbol, other.currency.symbol))
125         return self.amount == other.amount
126     def __ne__(self, other):
127         if self.currency != other.currency:
128             raise ValueError("cannot compare %s with %s" % (self.currency.symbol, other.currency.symbol))
129         return self.amount != other.amount
130     def __lt__(self, other):
131         if self.currency != other.currency:
132             raise ValueError("cannot compare %s with %s" % (self.currency.symbol, other.currency.symbol))
133         return self.amount < other.amount
134     def __le__(self, other):
135         if self.currency != other.currency:
136             raise ValueError("cannot compare %s with %s" % (self.currency.symbol, other.currency.symbol))
137         return self.amount <= other.amount
138     def __gt__(self, other):
139         if self.currency != other.currency:
140             raise ValueError("cannot compare %s with %s" % (self.currency.symbol, other.currency.symbol))
141         return self.amount > other.amount
142     def __ge__(self, other):
143         if self.currency != other.currency:
144             raise ValueError("cannot compare %s with %s" % (self.currency.symbol, other.currency.symbol))
145         return self.amount >= other.amount