1 /*
2 * Copyright 2000-2004 Michael Edwardes <mte@users.sourceforge.net>
3 * Copyright 2001-2017 Thomas Baumgart <tbaumgart@kde.org>
4 * Copyright 2001-2002 Felix Rodriguez <frodriguez@users.sourceforge.net>
5 * Copyright 2002-2004 Kevin Tambascio <ktambascio@users.sourceforge.net>
6 * Copyright 2005 Ace Jones <acejones@users.sourceforge.net>
7 * Copyright 2007-2010 Alvaro Soliverez <asoliverez@gmail.com>
8 * Copyright 2011 Carlos Eduardo da Silva <kaduardo@gmail.com>
9 * Copyright 2017-2018 Łukasz Wojniłowicz <lukasz.wojnilowicz@gmail.com>
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License as
13 * published by the Free Software Foundation; either version 2 of
14 * the License, or (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see <http://www.gnu.org/licenses/>.
23 */
24
25 // make sure, that this is defined before we even include any other header file
26 #ifndef __STDC_LIMIT_MACROS
27 #define __STDC_LIMIT_MACROS // force definition of min and max values
28 #endif
29
30 #include "mymoneymoney.h"
31
32 #include <stdint.h>
33 #include <gmpxx.h>
34
35 // ----------------------------------------------------------------------------
36 // QT Includes
37
38 #include <QString>
39
40 // ----------------------------------------------------------------------------
41 // Project Includes
42
43 #include "mymoneyexception.h"
44 #include "mymoneyenums.h"
45
46 const MyMoneyMoney MyMoneyMoney::ONE = MyMoneyMoney(1, 1);
47 const MyMoneyMoney MyMoneyMoney::MINUS_ONE = MyMoneyMoney(-1, 1);
48 namespace eMyMoney
49 {
50 namespace Money {
51
52 enum fileVersionE : int {
53 FILE_4_BYTE_VALUE = 0,
54 FILE_8_BYTE_VALUE
55 };
56
57 QChar _thousandSeparator = QLatin1Char(',');
58 QChar _decimalSeparator = QLatin1Char('.');
59 eMyMoney::Money::signPosition _negativeMonetarySignPosition = PreceedQuantityAndSymbol;
60 eMyMoney::Money::signPosition _positiveMonetarySignPosition = PreceedQuantityAndSymbol;
61 bool _negativePrefixCurrencySymbol = false;
62 bool _positivePrefixCurrencySymbol = false;
63 bool _negativeSpaceSeparatesSymbol = true;
64 bool _positiveSpaceSeparatesSymbol = true;
65 eMyMoney::Money::fileVersionE _fileVersion = fileVersionE::FILE_4_BYTE_VALUE;
66 }
67 }
68
69 MyMoneyMoney MyMoneyMoney::maxValue = MyMoneyMoney(INT64_MAX, 100);
70 MyMoneyMoney MyMoneyMoney::minValue = MyMoneyMoney(INT64_MIN, 100);
71 MyMoneyMoney MyMoneyMoney::autoCalc = MyMoneyMoney(INT64_MIN + 1, 100);
72
setNegativeSpaceSeparatesSymbol(const bool flag)73 void MyMoneyMoney::setNegativeSpaceSeparatesSymbol(const bool flag)
74 {
75 eMyMoney::Money::_negativeSpaceSeparatesSymbol= flag;
76 }
77
setPositiveSpaceSeparatesSymbol(const bool flag)78 void MyMoneyMoney::setPositiveSpaceSeparatesSymbol(const bool flag)
79 {
80 eMyMoney::Money::_positiveSpaceSeparatesSymbol= flag;
81 }
82
setNegativePrefixCurrencySymbol(const bool flag)83 void MyMoneyMoney::setNegativePrefixCurrencySymbol(const bool flag)
84 {
85 eMyMoney::Money::_negativePrefixCurrencySymbol = flag;
86 }
87
setPositivePrefixCurrencySymbol(const bool flag)88 void MyMoneyMoney::setPositivePrefixCurrencySymbol(const bool flag)
89 {
90 eMyMoney::Money::_positivePrefixCurrencySymbol = flag;
91 }
92
setNegativeMonetarySignPosition(const eMyMoney::Money::signPosition pos)93 void MyMoneyMoney::setNegativeMonetarySignPosition(const eMyMoney::Money::signPosition pos)
94 {
95 eMyMoney::Money::_negativeMonetarySignPosition = pos;
96 }
97
negativeMonetarySignPosition()98 eMyMoney::Money::signPosition MyMoneyMoney::negativeMonetarySignPosition()
99 {
100 return eMyMoney::Money::_negativeMonetarySignPosition;
101 }
102
setPositiveMonetarySignPosition(const eMyMoney::Money::signPosition pos)103 void MyMoneyMoney::setPositiveMonetarySignPosition(const eMyMoney::Money::signPosition pos)
104 {
105 eMyMoney::Money::_positiveMonetarySignPosition = pos;
106 }
107
positiveMonetarySignPosition()108 eMyMoney::Money::signPosition MyMoneyMoney::positiveMonetarySignPosition()
109 {
110 return eMyMoney::Money::_positiveMonetarySignPosition;
111 }
112
setThousandSeparator(const QChar & separator)113 void MyMoneyMoney::setThousandSeparator(const QChar &separator)
114 {
115 if (separator != QLatin1Char(' '))
116 eMyMoney::Money::_thousandSeparator = separator;
117 else
118 eMyMoney::Money::_thousandSeparator = 0;
119 }
120
thousandSeparator()121 const QChar MyMoneyMoney::thousandSeparator()
122 {
123 return eMyMoney::Money::_thousandSeparator;
124 }
125
setDecimalSeparator(const QChar & separator)126 void MyMoneyMoney::setDecimalSeparator(const QChar &separator)
127 {
128 if (separator != QLatin1Char(' '))
129 eMyMoney::Money::_decimalSeparator = separator;
130 else
131 eMyMoney::Money::_decimalSeparator = 0;
132 }
133
decimalSeparator()134 const QChar MyMoneyMoney::decimalSeparator()
135 {
136 return eMyMoney::Money::_decimalSeparator;
137 }
138
MyMoneyMoney(const QString & pszAmount)139 MyMoneyMoney::MyMoneyMoney(const QString& pszAmount)
140 : AlkValue(pszAmount, eMyMoney::Money::_decimalSeparator)
141 {
142 }
143
144 ////////////////////////////////////////////////////////////////////////////////
145 // Name: MyMoneyMoney
146 // Purpose: Constructor - constructs object from an amount in a signed64 value
147 // Returns: None
148 // Throws: Nothing.
149 // Arguments: Amount - signed 64 object containing amount
150 // denom - denominator of the object
151 //
152 ////////////////////////////////////////////////////////////////////////////////
MyMoneyMoney(qint64 Amount,const unsigned int denom)153 MyMoneyMoney::MyMoneyMoney(qint64 Amount, const unsigned int denom)
154 {
155 if (denom == 0)
156 throw MYMONEYEXCEPTION_CSTRING("Denominator 0 not allowed!");
157
158 *this = AlkValue(QString::fromLatin1("%1/%2").arg(Amount).arg(denom), eMyMoney::Money::_decimalSeparator);
159 }
160
161 ////////////////////////////////////////////////////////////////////////////////
162 // Name: MyMoneyMoney
163 // Purpose: Constructor - constructs object from an amount in a integer value
164 // Returns: None
165 // Throws: Nothing.
166 // Arguments: iAmount - integer object containing amount
167 // denom - denominator of the object
168 //
169 ////////////////////////////////////////////////////////////////////////////////
MyMoneyMoney(const int iAmount,const unsigned int denom)170 MyMoneyMoney::MyMoneyMoney(const int iAmount, const unsigned int denom)
171 {
172 if (denom == 0)
173 throw MYMONEYEXCEPTION_CSTRING("Denominator 0 not allowed!");
174 *this = AlkValue(iAmount, denom);
175 }
176
177 ////////////////////////////////////////////////////////////////////////////////
178 // Name: MyMoneyMoney
179 // Purpose: Constructor - constructs object from an amount in a long integer value
180 // Returns: None
181 // Throws: Nothing.
182 // Arguments: iAmount - integer object containing amount
183 // denom - denominator of the object
184 //
185 ////////////////////////////////////////////////////////////////////////////////
MyMoneyMoney(const long int iAmount,const unsigned int denom)186 MyMoneyMoney::MyMoneyMoney(const long int iAmount, const unsigned int denom)
187 {
188 if (denom == 0)
189 throw MYMONEYEXCEPTION_CSTRING("Denominator 0 not allowed!");
190 *this = AlkValue(QString::fromLatin1("%1/%2").arg(iAmount).arg(denom), eMyMoney::Money::_decimalSeparator);
191 }
192
193
~MyMoneyMoney()194 MyMoneyMoney::~MyMoneyMoney()
195 {
196 }
197
abs() const198 MyMoneyMoney MyMoneyMoney::abs() const
199 {
200 return static_cast<const MyMoneyMoney>(AlkValue::abs());
201 }
202
formatMoney(int denom,bool showThousandSeparator) const203 QString MyMoneyMoney::formatMoney(int denom, bool showThousandSeparator) const
204 {
205 return formatMoney(QString(), denomToPrec(denom), showThousandSeparator);
206 }
207
formatMoney(const QString & currency,const int prec,bool showThousandSeparator) const208 QString MyMoneyMoney::formatMoney(const QString& currency, const int prec, bool showThousandSeparator) const
209 {
210 QString res;
211 QString tmpCurrency = currency;
212 int tmpPrec = prec;
213 mpz_class denom = 1;
214 mpz_class value;
215
216 // if prec == -1 we want the maximum possible but w/o trailing zeroes
217 if (tmpPrec > -1) {
218 while (tmpPrec--) {
219 denom *= 10;
220 }
221 } else {
222 // fix it to a max of 9 digits on the right side for now
223 denom = 1000000000;
224 }
225
226 // as long as AlkValue::convertDenominator() does not take an
227 // mpz_class as the denominator, we need to use a signed int
228 // and limit the precision to 9 digits (the max we can
229 // present with 31 bits
230 #if 1
231 // MPIR and GMP use different types for the return value of mpz_get_si()
232 // which causes warnings on some compilers.
233 #ifdef mpir_version // MPIR is used
234 mpir_si denominator;
235 #else // GMP is used
236 long int denominator;
237 #endif
238
239 if (mpz_fits_sint_p(denom.get_mpz_t())) {
240 denominator = mpz_get_si(denom.get_mpz_t());
241 } else {
242 denominator = 1000000000;
243 }
244 value = static_cast<const MyMoneyMoney>(convertDenominator(denominator)).valueRef().get_num();
245 #else
246 value = static_cast<const MyMoneyMoney>(convertDenominator(denom)).valueRef().get_num();
247 #endif
248
249 // Once we really support multiple currencies then this method will
250 // be much better than using KLocale::global()->formatMoney.
251 bool bNegative = false;
252 mpz_class left = value / static_cast<MyMoneyMoney>(convertDenominator(denominator)).valueRef().get_den();
253 mpz_class right = mpz_class((valueRef() - mpq_class(left)) * denom);
254
255 if (right < 0) {
256 right = -right;
257 bNegative = true;
258 }
259 if (left < 0) {
260 left = -left;
261 bNegative = true;
262 }
263
264 // convert the integer (left) part to a string
265 res.append(left.get_str().c_str());
266
267 // if requested, insert thousand separators every three digits
268 if (showThousandSeparator) {
269 int pos = res.length();
270 while ((0 < (pos -= 3)) && thousandSeparator() != 0)
271 res.insert(pos, thousandSeparator());
272 }
273
274 // take care of the fractional part
275 if (prec > 0 || (prec == -1 && right != 0)) {
276 if (decimalSeparator() != 0)
277 res += decimalSeparator();
278
279 auto rs = QString::fromLatin1("%1").arg(right.get_str().c_str());
280 if (prec != -1)
281 rs = rs.rightJustified(prec, QLatin1Char('0'), true);
282 else {
283 rs = rs.rightJustified(9, QLatin1Char('0'), true);
284 // no trailing zeroes or decimal separators
285 while (rs.endsWith(QLatin1Char('0')))
286 rs.truncate(rs.length() - 1);
287 while (rs.endsWith(decimalSeparator()))
288 rs.truncate(rs.length() - 1);
289 }
290 res += rs;
291 }
292
293 eMyMoney::Money::signPosition signpos = bNegative ? eMyMoney::Money::_negativeMonetarySignPosition : eMyMoney::Money::_positiveMonetarySignPosition;
294 auto sign = bNegative ? QString::fromLatin1("-") : QString();
295
296 switch (signpos) {
297 case eMyMoney::Money::ParensAround:
298 // do nothing here
299 break;
300 case eMyMoney::Money::PreceedQuantityAndSymbol:
301 res.prepend(sign);
302 break;
303 case eMyMoney::Money::SucceedQuantityAndSymbol:
304 res.append(sign);
305 break;
306 case eMyMoney::Money::PreceedSymbol:
307 tmpCurrency.prepend(sign);
308 break;
309 case eMyMoney::Money::SucceedSymbol:
310 tmpCurrency.append(sign);
311 break;
312 }
313 if (!tmpCurrency.isEmpty()) {
314 if (bNegative ? eMyMoney::Money::_negativePrefixCurrencySymbol : eMyMoney::Money::_positivePrefixCurrencySymbol) {
315 if (bNegative ? eMyMoney::Money::_negativeSpaceSeparatesSymbol : eMyMoney::Money::_positiveSpaceSeparatesSymbol) {
316 res.prepend(QLatin1Char(' '));
317 }
318 res.prepend(tmpCurrency);
319 } else {
320 if (bNegative ? eMyMoney::Money::_negativeSpaceSeparatesSymbol : eMyMoney::Money::_positiveSpaceSeparatesSymbol) {
321 res.append(QLatin1Char(' '));
322 }
323 res.append(tmpCurrency);
324 }
325 }
326
327 if (signpos == eMyMoney::Money::ParensAround) {
328 res.prepend(QLatin1Char('('));
329 res.append(QLatin1Char(')'));
330 }
331
332 return res;
333 }
334
335 ////////////////////////////////////////////////////////////////////////////////
336 // Name: operator+
337 // Purpose: Addition operator - adds the input amount to the object
338 // Returns: The current object
339 // Throws: Nothing.
340 // Arguments: b - MyMoneyMoney object to be added
341 //
342 ////////////////////////////////////////////////////////////////////////////////
operator +(const MyMoneyMoney & _b) const343 const MyMoneyMoney MyMoneyMoney::operator+(const MyMoneyMoney& _b) const
344 {
345 return static_cast<const MyMoneyMoney>(AlkValue::operator+(_b));
346 }
347
348
349 ////////////////////////////////////////////////////////////////////////////////
350 // Name: operator-
351 // Purpose: Addition operator - subtracts the input amount from the object
352 // Returns: The current object
353 // Throws: Nothing.
354 // Arguments: AmountInPence - MyMoneyMoney object to be subtracted
355 //
356 ////////////////////////////////////////////////////////////////////////////////
operator -(const MyMoneyMoney & _b) const357 const MyMoneyMoney MyMoneyMoney::operator-(const MyMoneyMoney& _b) const
358 {
359 return static_cast<const MyMoneyMoney>(AlkValue::operator-(_b));
360 }
361
362 ////////////////////////////////////////////////////////////////////////////////
363 // Name: operator*
364 // Purpose: Multiplication operator - multiplies the input amount to the object
365 // Returns: The current object
366 // Throws: Nothing.
367 // Arguments: b - MyMoneyMoney object to be multiplied
368 //
369 ////////////////////////////////////////////////////////////////////////////////
operator *(const MyMoneyMoney & _b) const370 const MyMoneyMoney MyMoneyMoney::operator*(const MyMoneyMoney& _b) const
371 {
372 return static_cast<const MyMoneyMoney>(AlkValue::operator*(_b));
373 }
374
375 ////////////////////////////////////////////////////////////////////////////////
376 // Name: operator/
377 // Purpose: Division operator - divides the object by the input amount
378 // Returns: The current object
379 // Throws: Nothing.
380 // Arguments: b - MyMoneyMoney object to be used as dividend
381 //
382 ////////////////////////////////////////////////////////////////////////////////
operator /(const MyMoneyMoney & _b) const383 const MyMoneyMoney MyMoneyMoney::operator/(const MyMoneyMoney& _b) const
384 {
385 return static_cast<const MyMoneyMoney>(AlkValue::operator/(_b));
386 }
387
isNegative() const388 bool MyMoneyMoney::isNegative() const
389 {
390 return (valueRef() < 0) ? true : false;
391 }
392
isPositive() const393 bool MyMoneyMoney::isPositive() const
394 {
395 return (valueRef() > 0) ? true : false;
396 }
397
isZero() const398 bool MyMoneyMoney::isZero() const
399 {
400 return valueRef() == 0;
401 }
402
isAutoCalc() const403 bool MyMoneyMoney::isAutoCalc() const
404 {
405 return (*this == autoCalc);
406 }
407
convert(const signed64 _denom,const AlkValue::RoundingMethod how) const408 MyMoneyMoney MyMoneyMoney::convert(const signed64 _denom, const AlkValue::RoundingMethod how) const
409 {
410 return static_cast<const MyMoneyMoney>(convertDenominator(_denom, how));
411 }
412
reduce() const413 MyMoneyMoney MyMoneyMoney::reduce() const
414 {
415 MyMoneyMoney out(*this);
416 out.canonicalize();
417 return out;
418 }
419
precToDenom(int prec)420 signed64 MyMoneyMoney::precToDenom(int prec)
421 {
422 signed64 denom = 1;
423
424 while (prec--)
425 denom *= 10;
426
427 return denom;
428 }
429
toDouble() const430 double MyMoneyMoney::toDouble() const
431 {
432 return valueRef().get_d();
433 }
434
denomToPrec(signed64 fract)435 int MyMoneyMoney::denomToPrec(signed64 fract)
436 {
437 int rc = 0;
438 while (fract > 1) {
439 rc++;
440 fract /= 10;
441 }
442 return rc;
443 }
444
445