File indexing completed on 2024-05-12 16:42:03

0001 /*
0002     SPDX-FileCopyrightText: 2004-2018 Thomas Baumgart <tbaumgart@kde.org>
0003     SPDX-FileCopyrightText: 2017 Łukasz Wojniłowicz <lukasz.wojnilowicz@gmail.com>
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "kcurrencycalculator.h"
0008 
0009 // ----------------------------------------------------------------------------
0010 // QT Includes
0011 
0012 #include <QButtonGroup>
0013 #include <QLabel>
0014 #include <QRadioButton>
0015 #include <QCheckBox>
0016 #include <QPushButton>
0017 #include <QDialogButtonBox>
0018 #include <QPointer>
0019 
0020 // ----------------------------------------------------------------------------
0021 // KDE Includes
0022 
0023 #include <KLocalizedString>
0024 
0025 // ----------------------------------------------------------------------------
0026 // Project Includes
0027 
0028 #include "ui_kcurrencycalculator.h"
0029 
0030 #include "mymoneyfile.h"
0031 #include "mymoneyaccount.h"
0032 #include "mymoneysecurity.h"
0033 #include "mymoneyprice.h"
0034 #include "mymoneymoney.h"
0035 #include "mymoneyexception.h"
0036 #include "mymoneysplit.h"
0037 #include "mymoneytransaction.h"
0038 #include "kmymoneysettings.h"
0039 
0040 class KCurrencyCalculatorPrivate
0041 {
0042     Q_DISABLE_COPY(KCurrencyCalculatorPrivate)
0043     Q_DECLARE_PUBLIC(KCurrencyCalculator)
0044 
0045 public:
0046     explicit KCurrencyCalculatorPrivate(KCurrencyCalculator *qq,
0047                                         const MyMoneySecurity& from,
0048                                         const MyMoneySecurity& to,
0049                                         const MyMoneyMoney& value,
0050                                         const MyMoneyMoney& shares,
0051                                         const QDate& date,
0052                                         const signed64 resultFraction) :
0053         q_ptr(qq),
0054         ui(new Ui::KCurrencyCalculator),
0055         m_fromCurrency(from),
0056         m_toCurrency(to),
0057         m_result(shares.abs()),
0058         m_value(value.abs()),
0059         m_date(date),
0060         m_resultFraction(resultFraction)
0061     {
0062     }
0063 
0064     ~KCurrencyCalculatorPrivate()
0065     {
0066         delete ui;
0067     }
0068 
0069     void init()
0070     {
0071         Q_Q(KCurrencyCalculator);
0072         ui->setupUi(q);
0073         auto file = MyMoneyFile::instance();
0074 
0075         //set main widget of QDialog
0076         ui->buttonGroup1->setId(ui->m_amountButton, 0);
0077         ui->buttonGroup1->setId(ui->m_rateButton, 1);
0078 
0079         ui->m_dateFrame->hide();
0080         if (m_date.isValid())
0081             ui->m_dateEdit->setDate(m_date);
0082         else
0083             ui->m_dateEdit->setDate(QDate::currentDate());
0084 
0085         ui->m_fromCurrencyText->setText(QString(MyMoneySecurity::securityTypeToString(m_fromCurrency.securityType()) + ' ' + (m_fromCurrency.isCurrency() ? m_fromCurrency.id() : m_fromCurrency.tradingSymbol())));
0086         ui->m_toCurrencyText->setText(QString(MyMoneySecurity::securityTypeToString(m_toCurrency.securityType()) + ' ' + (m_toCurrency.isCurrency() ? m_toCurrency.id() : m_toCurrency.tradingSymbol())));
0087 
0088         //set bold font
0089         auto boldFont = ui->m_fromCurrencyText->font();
0090         boldFont.setBold(true);
0091         ui->m_fromCurrencyText->setFont(boldFont);
0092         boldFont = ui->m_toCurrencyText->font();
0093         boldFont.setBold(true);
0094         ui->m_toCurrencyText->setFont(boldFont);
0095 
0096         ui->m_fromAmount->setText(m_value.formatMoney(QString(), MyMoneyMoney::denomToPrec(m_fromCurrency.smallestAccountFraction())));
0097 
0098         ui->m_dateText->setText(QLocale().toString(m_date));
0099 
0100         ui->m_updateButton->setChecked(KMyMoneySettings::priceHistoryUpdate());
0101 
0102         // setup initial result
0103         if (m_result == MyMoneyMoney() && !m_value.isZero()) {
0104             const MyMoneyPrice &pr = file->price(m_fromCurrency.id(), m_toCurrency.id(), m_date);
0105             if (pr.isValid()) {
0106                 m_result = m_value * pr.rate(m_toCurrency.id());
0107             }
0108         }
0109 
0110         // fill in initial values
0111         ui->m_toAmount->setPrecision(-1);
0112         ui->m_toAmount->setValue(m_result);
0113 
0114         ui->m_conversionRate->setPrecision(-1);
0115 
0116         q->connect(ui->m_amountButton, &QAbstractButton::clicked, q, &KCurrencyCalculator::slotSetToAmount);
0117         q->connect(ui->m_rateButton, &QAbstractButton::clicked, q, &KCurrencyCalculator::slotSetExchangeRate);
0118 
0119         q->connect(ui->m_toAmount, &AmountEdit::textChanged, q, &KCurrencyCalculator::slotUpdateResult);
0120         q->connect(ui->m_conversionRate, &AmountEdit::textChanged, q, &KCurrencyCalculator::slotUpdateRate);
0121 
0122         q->connect(ui->m_toAmount, &AmountEdit::returnPressed, q, &KCurrencyCalculator::accept);
0123         q->connect(ui->m_conversionRate, &AmountEdit::returnPressed, q, &KCurrencyCalculator::accept);
0124 
0125         // use this as the default
0126         ui->m_amountButton->animateClick();
0127         q->slotUpdateResult(ui->m_toAmount->text());
0128 
0129         // If the from security is not a currency, we only allow entering a price
0130         if (!m_fromCurrency.isCurrency()) {
0131             ui->m_rateButton->animateClick();
0132             ui->m_amountButton->hide();
0133             ui->m_toAmount->hide();
0134         }
0135     }
0136 
0137     void updateExample(const MyMoneyMoney& price)
0138     {
0139         QString msg;
0140         if (price.isZero()) {
0141             msg = QString("1 %1 = ? %2").arg(m_fromCurrency.tradingSymbol())
0142                   .arg(m_toCurrency.tradingSymbol());
0143             if (m_fromCurrency.isCurrency()) {
0144                 msg += QString("\n");
0145                 msg += QString("1 %1 = ? %2").arg(m_toCurrency.tradingSymbol())
0146                        .arg(m_fromCurrency.tradingSymbol());
0147             }
0148         } else {
0149             msg = QString("1 %1 = %2 %3").arg(m_fromCurrency.tradingSymbol())
0150                   .arg(price.formatMoney(QString(), m_fromCurrency.pricePrecision()))
0151                   .arg(m_toCurrency.tradingSymbol());
0152             if (m_fromCurrency.isCurrency()) {
0153                 msg += QString("\n");
0154                 msg += QString("1 %1 = %2 %3").arg(m_toCurrency.tradingSymbol())
0155                        .arg((MyMoneyMoney::ONE / price).formatMoney(QString(), m_toCurrency.pricePrecision()))
0156                        .arg(m_fromCurrency.tradingSymbol());
0157             }
0158         }
0159         ui->m_conversionExample->setText(msg);
0160         ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(!price.isZero());
0161     }
0162 
0163     KCurrencyCalculator     *q_ptr;
0164     Ui::KCurrencyCalculator *ui;
0165     MyMoneySecurity          m_fromCurrency;
0166     MyMoneySecurity          m_toCurrency;
0167     MyMoneyMoney             m_result;
0168     MyMoneyMoney             m_value;
0169     QDate                    m_date;
0170     signed64                 m_resultFraction;
0171 };
0172 
0173 KCurrencyCalculator::KCurrencyCalculator(const MyMoneySecurity& from,
0174         const MyMoneySecurity& to,
0175         const MyMoneyMoney& value,
0176         const MyMoneyMoney& shares,
0177         const QDate& date,
0178         const signed64 resultFraction,
0179         QWidget *parent) :
0180     QDialog(parent),
0181     d_ptr(new KCurrencyCalculatorPrivate(this,
0182                                          from,
0183                                          to,
0184                                          value,
0185                                          shares,
0186                                          date,
0187                                          resultFraction))
0188 {
0189     Q_D(KCurrencyCalculator);
0190     d->init();
0191 }
0192 
0193 KCurrencyCalculator::~KCurrencyCalculator()
0194 {
0195     Q_D(KCurrencyCalculator);
0196     delete d;
0197 }
0198 
0199 bool KCurrencyCalculator::setupSplitPrice(MyMoneyMoney& shares,
0200         const MyMoneyTransaction& t,
0201         const MyMoneySplit& s,
0202         const QMap<QString,
0203         MyMoneyMoney>& priceInfo,
0204         QWidget* parentWidget)
0205 {
0206     auto rc = true;
0207     auto file = MyMoneyFile::instance();
0208 
0209     if (!s.value().isZero()) {
0210         auto cat = file->account(s.accountId());
0211         MyMoneySecurity toCurrency;
0212         toCurrency = file->security(cat.currencyId());
0213         // determine the fraction required for this category/account
0214         int fract = cat.fraction(toCurrency);
0215 
0216         if (cat.currencyId() != t.commodity()) {
0217 
0218             MyMoneyMoney toValue;
0219             auto fromCurrency = file->security(t.commodity());
0220             // display only positive values to the user
0221             auto fromValue = s.value().abs();
0222 
0223             // if we had a price info in the beginning, we use it here
0224             if (priceInfo.find(cat.currencyId()) != priceInfo.end()) {
0225                 toValue = (fromValue * priceInfo[cat.currencyId()]).convert(fract);
0226             }
0227             // if the shares are still 0, we need to change that
0228             if (toValue.isZero()) {
0229                 const MyMoneyPrice &price = file->price(fromCurrency.id(), toCurrency.id(), t.postDate());
0230                 // if the price is valid calculate the shares. If it is invalid
0231                 // assume a conversion rate of 1.0
0232                 if (price.isValid()) {
0233                     toValue = (price.rate(toCurrency.id()) * fromValue).convert(fract);
0234                 } else {
0235                     toValue = fromValue;
0236                 }
0237             }
0238 
0239             // now present all that to the user
0240             QPointer<KCurrencyCalculator> calc =
0241                 new KCurrencyCalculator(fromCurrency,
0242                                         toCurrency,
0243                                         fromValue,
0244                                         toValue,
0245                                         t.postDate(),
0246                                         10000000000,
0247                                         parentWidget);
0248 
0249             if (calc->exec() == QDialog::Rejected) {
0250                 rc = false;
0251             } else
0252                 shares = (s.value() * calc->price()).convert(fract);
0253 
0254             delete calc;
0255 
0256         } else {
0257             shares = s.value().convert(fract);
0258         }
0259     } else
0260         shares = s.value();
0261 
0262     return rc;
0263 }
0264 
0265 void KCurrencyCalculator::setupPriceEditor()
0266 {
0267     Q_D(KCurrencyCalculator);
0268     d->ui->m_dateFrame->show();
0269     d->ui->m_amountDateFrame->hide();
0270     d->ui->m_updateButton->setChecked(true);
0271     d->ui->m_updateButton->hide();
0272 }
0273 
0274 void KCurrencyCalculator::slotSetToAmount()
0275 {
0276     Q_D(KCurrencyCalculator);
0277     d->ui->m_rateButton->setChecked(false);
0278     d->ui->m_toAmount->setEnabled(true);
0279     d->ui->m_conversionRate->setEnabled(false);
0280 }
0281 
0282 void KCurrencyCalculator::slotSetExchangeRate()
0283 {
0284     Q_D(KCurrencyCalculator);
0285     d->ui->m_amountButton->setChecked(false);
0286     d->ui->m_toAmount->setEnabled(false);
0287     d->ui->m_conversionRate->setEnabled(true);
0288 }
0289 
0290 void KCurrencyCalculator::slotUpdateResult(const QString& /*txt*/)
0291 {
0292     Q_D(KCurrencyCalculator);
0293     MyMoneyMoney result = d->ui->m_toAmount->value();
0294     MyMoneyMoney price(MyMoneyMoney::ONE);
0295 
0296     if (result.isNegative()) {
0297         d->ui->m_toAmount->setValue(-result);
0298         return;
0299     }
0300 
0301     if (!result.isZero() && !d->m_value.isZero()) {
0302         price = result / d->m_value;
0303 
0304         QSignalBlocker signalBlocker(d->ui->m_conversionRate);
0305         d->ui->m_conversionRate->setValue(price);
0306         d->m_result = (d->m_value * price).convert(d->m_resultFraction);
0307     }
0308     d->updateExample(price);
0309 }
0310 
0311 void KCurrencyCalculator::slotUpdateRate(const QString& /*txt*/)
0312 {
0313     Q_D(KCurrencyCalculator);
0314     auto price = d->ui->m_conversionRate->value();
0315 
0316     if (price.isNegative()) {
0317         d->ui->m_conversionRate->setValue(-price);
0318         return;
0319     }
0320 
0321     if (!price.isZero()) {
0322         QSignalBlocker signalBlocker(d->ui->m_toAmount);
0323         d->m_result = (d->m_value * price).convert(d->m_resultFraction);
0324         d->ui->m_toAmount->setValue(d->m_result);
0325     }
0326     d->updateExample(price);
0327 }
0328 
0329 void KCurrencyCalculator::accept()
0330 {
0331     Q_D(KCurrencyCalculator);
0332     if (d->ui->m_conversionRate->isEnabled())
0333         slotUpdateRate(QString());
0334     else
0335         slotUpdateResult(QString());
0336 
0337     if (d->ui->m_updateButton->isChecked()) {
0338         auto pr = MyMoneyFile::instance()->price(d->m_fromCurrency.id(), d->m_toCurrency.id(), d->ui->m_dateEdit->date());
0339         if (!pr.isValid() //
0340                 || pr.date() != d->ui->m_dateEdit->date() //
0341                 || (pr.date() == d->ui->m_dateEdit->date() && pr.rate(d->m_fromCurrency.id()) != price())) {
0342             pr = MyMoneyPrice(d->m_fromCurrency.id(), d->m_toCurrency.id(), d->ui->m_dateEdit->date(), price(), i18n("User"));
0343             MyMoneyFileTransaction ft;
0344             try {
0345                 MyMoneyFile::instance()->addPrice(pr);
0346                 ft.commit();
0347             } catch (const MyMoneyException &) {
0348                 qDebug("Cannot add price");
0349             }
0350         }
0351     }
0352 
0353     // remember setting for next round
0354     KMyMoneySettings::setPriceHistoryUpdate(d->ui->m_updateButton->isChecked());
0355     QDialog::accept();
0356 }
0357 
0358 MyMoneyMoney KCurrencyCalculator::price() const
0359 {
0360     Q_D(const KCurrencyCalculator);
0361     // This should fix https://bugs.kde.org/show_bug.cgi?id=205254 and
0362     // https://bugs.kde.org/show_bug.cgi?id=325953 as well as
0363     // https://bugs.kde.org/show_bug.cgi?id=300965
0364     if (d->ui->m_amountButton->isChecked())
0365         return d->m_result.abs() / d->m_value.abs();
0366     else
0367         return d->ui->m_conversionRate->value();
0368 }