https://bugs.kde.org/show_bug.cgi?id=526089

            Bug ID: 526089
           Summary: Forecast: scheduled transfer into an investment/stock
                    account adds the raw share count instead of a
                    currency-converted value, using a share count frozen
                    at schedule-edit time
    Classification: Applications
           Product: kmymoney
      Version First 5.2.2
       Reported In:
          Platform: Other
                OS: Linux
            Status: REPORTED
          Severity: normal
          Priority: NOR
         Component: bugtracker
          Assignee: [email protected]
          Reporter: [email protected]
  Target Milestone: ---

Created attachment 196449
  --> https://bugs.kde.org/attachment.cgi?id=196449&action=edit
scheduled transfer into an investment/stock account

Summary
A scheduled transaction ("Schedule") whose source account is a regular
(non-investment) account and whose target ("Transfer to") is a stock/investment
account can be created, even though the schedule editor otherwise blocks
investment/stock accounts. When such a schedule is processed by the Forecast
feature, two things go wrong:

The number of shares used for every future occurrence is fixed once, at the
time the schedule is created or last edited, from whatever price happened to be
in the price history at that moment — it is never re-evaluated for the actual
future due date.
Worse, the forecast code adds that raw share count directly to an account
balance array that is otherwise expressed in the account's trading currency,
without converting shares to a monetary value. This mixes units (shares vs.
currency) and produces an incorrect forecasted balance for the investment/stock
account.
Steps to reproduce
Create (or use) a stock/investment account, e.g. an ETF, held in a currency-
denominated parent investment account.
Create a new Schedule (Account → Schedules → New Schedule):
Payment type: "Transfer" ("Umbuchung")
Account (source): a normal asset account, e.g. a savings/checking account
Transfer to / Category ("Umbuchung auf"): the stock/investment account
Amount: a fixed value, e.g. 500 in the account's currency
Occurs monthly (or any recurrence)
Save the schedule. (Note: this succeeds even though the "Account" combo box
explicitly excludes Investment/Stock account types — the "Transfer to" combo
does not have the same restriction.)
Open the Forecast view (or otherwise let the forecast engine include this
account).
Observe the projected balance of the stock/investment account over the forecast
period.
Expected behavior
A scheduled transfer into an investment/stock account is currently the only way
to plan a recurring security purchase in KMyMoney at all — the schedule editor
explicitly blocks investment/stock accounts as the schedule's own account (see
below), so this transfer-based workaround is the sole path users have for e.g.
a recurring ETF savings plan. This should keep working. The Forecast should
therefore convert the share count to the account's currency using an
appropriate price (e.g. the current price, the same one setStartingBalance()
already uses) before adding it to the projected balance, instead of adding the
raw share count.

Please do not "fix" this by simply excluding such transfers from the forecast
(or by blocking the transfer target combo the same way the source combo is
blocked) — that would remove the only existing way to model a recurring
investment purchase and would be a regression for anyone currently relying on
it, without addressing the underlying missing unit conversion.

Actual behavior
The number of shares is computed once, when the schedule is created/edited,
from whatever price is found in the price history at that time — silently,
without asking the user, if a cached/historical price already exists for the
security. This fixed share count is stored in the schedule's split and reused
unchanged for every future occurrence in the forecast, regardless of how far in
the future that occurrence is.
The Forecast engine then adds this raw share count directly to the account's
daily balance array, which for investment accounts is otherwise expressed in
currency (e.g. EUR), not in shares. The result is a forecasted balance that
mixes currency values and share counts, producing incorrect numbers.
Root cause / code pointers
All paths relative to the kmymoney source tree.

kmymoney/views/keditscheduledlg.cpp:207-211 — only the source account combo
excludes Investment/Stock accounts:

// don't show any investment account in the account combo
model->removeAccountType(eMyMoney::Account::Type::Investment);
model->removeAccountType(eMyMoney::Account::Type::Stock);
The categoryCombo (target/"transfer to") set up starting at line 219 has no
such restriction, so a stock/investment account can be chosen as the transfer
target.

kmymoney/views/newtransactioneditor.cpp:446-456 — when the target account's
currency differs from the transaction currency (i.e. it's a security), the
editor calls q->updateConversionRate(ui->creditDebitEdit) to derive the share
count from the entered amount.

kmymoney/dialogs/kcurrencyconverter.cpp:56-121 (KCurrencyConverter::updateRate)
— looks up MyMoneyFile::instance()->price(fromSecurity.id(), toSecurity.id(),
date). If a price is already known for that date, it is used without prompting
the user; the interactive "Kursrechner" dialog only opens when no price is
found. Either way, the resulting share count is written into the schedule's
split at edit time and never revisited.

kmymoney/mymoney/mymoneyforecast.cpp:359 — addScheduledTransactions() only
excludes a schedule if its own source account is of type Investment:

if (acc.accountType() != eMyMoney::Account::Type::Investment) {
A schedule whose source account is a normal account (as in the reproduction
steps) passes this check even though one of its splits targets an
investment/stock account.

kmymoney/mymoney/mymoneyforecast.cpp:387-406 — for every split of the
schedule's template transaction that belongs to a forecast account, the code
does:

if (accountFromSplit.accountType() == eMyMoney::Account::Type::Income) {
    balance[forecastDate] += (split.shares() * MyMoneyMoney::MINUS_ONE);
} else {
    balance[forecastDate] += split.shares();
}
split.shares() is added as-is. For the stock/investment split this is a share
count, not a currency amount — there is no multiplication by a price/rate here.

Compare with kmymoney/mymoney/mymoneyforecast.cpp:477-490
(setStartingBalance()), which does perform this conversion for the account's
starting balance:

const MyMoneyPrice& price = file->price(undersecurity.id(),
undersecurity.tradingCurrency(), m_evaluationDate);
if (price.isValid()) {
    rate = price.rate(undersecurity.tradingCurrency());
}
m_accountList[acc.id()][m_evaluationDate] = file->balance(acc.id(),
m_evaluationDate) * rate;
The scheduled-transaction path never applies an equivalent conversion, which is
the unit mismatch described above.

kmymoney/mymoney/mymoneyforecast.cpp:1172-1189 (forecastBalance()) returns
m_accountList[acc.id()][forecastDate] verbatim, with no later conversion step
either — so the mismatch is not corrected downstream.

Suggested fix direction
In addScheduledTransactions(), when a split's account is an investment/stock
account, convert split.shares() to the account's currency using an appropriate
price (e.g. the current price, mirroring what setStartingBalance() already
does) before adding it to balance[forecastDate], instead of adding the raw
share count.

Explicitly not suggested: excluding such schedules from the forecast, or
blocking investment/stock accounts as a transfer target in the schedule editor.
Both would just remove the only existing way to plan a recurring security
purchase, rather than fixing the actual bug (missing currency conversion /
stale share count).

Environment
kmymoney source checked out at /home/michael/git/kmymoney, commit
d99cfc2ab72a0fde5e9ba5e3b5a3bc04cc688a86 (git describe: v5.1.95-948-gd99cfc2ab,
2026-08-22).

-- 
You are receiving this mail because:
You are watching all bug changes.

Reply via email to