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 }