Bug 793306 - Price is not imported from CSV

This is really a variation of bug 796955, except that in this case
even less data was provided in the csv file, so we needed
extra code to defer second split generation to the generic import
matcher.

The missing details can be either transfer account,
transfer amount and/or price.

The generic import matcher has more options to find this
information: the transfer account can be guessed from
import maps or manually selected by the user.
Only when that one is known, we can consider transfer
amount. In a single currency situation, this is easy.
In the multi-currency case we currently need either
a price or a transfer amount from the csv import data.
This commit is contained in:
Geert Janssens 2023-02-05 18:55:55 +01:00
parent 732a005710
commit b862142e82
5 changed files with 59 additions and 27 deletions

View File

@ -2101,6 +2101,8 @@ CsvImpTransAssist::assist_match_page_prepare ()
draft_trans->m_price ? static_cast<gnc_numeric>(*draft_trans->m_price) : gnc_numeric{0, 0},
draft_trans->m_taction ? draft_trans->m_taction->c_str() : nullptr,
draft_trans->m_tmemo ? draft_trans->m_tmemo->c_str() : nullptr,
draft_trans->m_tamount ? static_cast<gnc_numeric>(*draft_trans->m_tamount) : gnc_numeric{0, 0},
draft_trans->m_taccount ? *draft_trans->m_taccount : nullptr,
draft_trans->m_trec_state ? *draft_trans->m_trec_state : '\0',
draft_trans->m_trec_date ? static_cast<time64>(GncDateTime(*draft_trans->m_trec_date, DayPart::neutral)) : 0,
};

View File

@ -674,6 +674,7 @@ void GncPreSplit::create_split (std::shared_ptr<DraftTransaction> draft_trans)
return;
}
auto splits_created = 0;
Account *account = nullptr;
Account *taccount = nullptr;
auto amount = GncNumeric();
@ -687,6 +688,16 @@ void GncPreSplit::create_split (std::shared_ptr<DraftTransaction> draft_trans)
if (m_amount_neg)
amount -= *m_amount_neg;
boost::optional<GncNumeric> tamount;
if (m_tamount || m_tamount_neg)
{
tamount = GncNumeric();
if (m_tamount)
*tamount += *m_tamount;
if (m_tamount_neg)
*tamount -= *m_tamount_neg;
}
/* Add a split with the cumulative amount value. */
// FIXME The first split is always assumed to be in the transaction currency, but this assumption
// may not hold in case of stock transactions.
@ -695,23 +706,16 @@ void GncPreSplit::create_split (std::shared_ptr<DraftTransaction> draft_trans)
if (m_price)
inv_price = m_price->inv();
trans_add_split (draft_trans->trans, account, amount, amount, m_action, m_memo, m_rec_state, m_rec_date);
splits_created++;
if (taccount)
{
/* If a taccount is set that forcibly means we're processing a single-line transaction
* Determine the transfer amount. If the csv data had columns for it, use those, otherwise
* assume transfer amount is */
auto tamount = GncNumeric();
auto tvalue = -tamount;
if (m_tamount || m_tamount_neg)
{
if (m_tamount)
tamount += *m_tamount;
if (m_tamount_neg)
tamount -= *m_tamount_neg;
tvalue = -amount;
}
else
* try to calculate it. The easiest is the single-currency case: just use the negated
* amount. In case of multi-currency, attempt to get a price and work from there. */
auto tvalue = -amount;
if (!tamount)
{
auto trans_curr = xaccTransGetCurrency(draft_trans->trans);
auto acct_comm = xaccAccountGetCommodity(taccount);
@ -739,24 +743,31 @@ void GncPreSplit::create_split (std::shared_ptr<DraftTransaction> draft_trans)
tamount = -amount * rate;
}
else
{
PWARN("No price found, not creating second split.");
/* Set bogus value for tamount so we can skip creation further down */
// FIXME should somehow pass the account to generic import matcher
// so it can have a shot a asking for an exchange rate and
// creating the split properly
tamount = -tvalue;
}
PWARN("No price found, defer creation of second split to generic import matcher.");
}
}
if (tamount != -tvalue)
trans_add_split (draft_trans->trans, taccount, tamount, tvalue, m_taction, m_tmemo, m_trec_state, m_trec_date);
if (tamount)
{
trans_add_split (draft_trans->trans, taccount, *tamount, tvalue, m_taction, m_tmemo, m_trec_state, m_trec_date);
splits_created++;
}
}
else
if (splits_created == 1)
{
/* If we get here, we're either
* - in multi-line mode
* - or single-line mode but didn't have enough details to create the
* transfer split.
* For the latter we will pass what we know about the transfer split to
* allow the generic import matcher to ask the user for the final
* details before creating this split.
*/
draft_trans->m_price = m_price;
draft_trans->m_taction = m_taction;
draft_trans->m_tmemo = m_tmemo;
draft_trans->m_tamount = tamount;
draft_trans->m_taccount = m_taccount;
draft_trans->m_trec_state = m_trec_state;
draft_trans->m_trec_date = m_trec_date;
}

View File

@ -130,6 +130,8 @@ struct DraftTransaction
boost::optional<GncNumeric> m_price;
boost::optional<std::string> m_taction;
boost::optional<std::string> m_tmemo;
boost::optional<GncNumeric> m_tamount;
boost::optional<Account*> m_taccount;
boost::optional<char> m_trec_state;
boost::optional<GncDate> m_trec_date;

View File

@ -104,8 +104,8 @@ struct _transactioninfo
/* When updating a matched transaction, append Description and Notes instead of replacing */
gboolean append_text;
/* Extra data we can use to build the balancing split. It may passed on by the
* code that calls the generic importer */
/* Extra data we can use to build the balancing split. It may be passed on by the
* code that calls the generic importer */
gnc_numeric lsplit_price;
char *lsplit_action;
char *lsplit_memo;
@ -113,10 +113,12 @@ struct _transactioninfo
time64 lsplit_rec_date;
gnc_numeric lsplit_value;
/* Amount for the balancing split. This can only be calculated when
/* Amount for the balancing split. This may be passed by the import front-
* ends or calculated. The latter is only possible when
* the destination account is known and may require an exchange rate
* if that account is not in the same commodity as the transaction. */
gnc_numeric lsplit_amount;
gboolean lsplit_amount_selected_manually;
};
/* Some simple getters and setters for the above data types. */
@ -288,6 +290,12 @@ gnc_import_TransInfo_set_last_split_info (GNCImportTransInfo *info,
info->lsplit_price = lsplit->price;
info->lsplit_action = g_strdup(lsplit->action);
info->lsplit_memo = g_strdup(lsplit->memo);
if (gnc_numeric_check (lsplit->amount) == 0)
{
info->lsplit_amount = lsplit->amount;
info->lsplit_amount_selected_manually = true;
}
info->dest_acc = lsplit->account;
info->lsplit_rec_state = lsplit->rec_state;
info->lsplit_rec_date = lsplit->rec_date;
}
@ -1089,7 +1097,9 @@ gboolean gnc_import_exists_online_id (Transaction *trans, GHashTable* acct_id_ha
static void trans_info_calculate_dest_amount (GNCImportTransInfo *info)
{
info->lsplit_value = gnc_numeric_neg (xaccTransGetImbalanceValue (info->trans));
info->lsplit_amount = {0, 1};
if (!info->lsplit_amount_selected_manually)
info->lsplit_amount = {0, 1};
if (info->dest_acc)
{
auto tcurr = xaccTransGetCurrency(info->trans);
@ -1100,6 +1110,11 @@ static void trans_info_calculate_dest_amount (GNCImportTransInfo *info)
if (gnc_commodity_equiv(tcurr, dcurr))
info->lsplit_amount = info->lsplit_value;
else if (info->lsplit_amount_selected_manually &&
gnc_numeric_check(info->lsplit_amount) == 0)
{
/* Nothing to do, user has provided amount already */
}
else if (gnc_numeric_check(info->lsplit_price) == 0)
{
/* We are in a multi currency situation and have a valid price */

View File

@ -51,6 +51,8 @@ typedef struct _lsplitinfo
gnc_numeric price;
const char *action;
const char *memo;
gnc_numeric amount;
Account *account;
char rec_state;
time64 rec_date;
} GNCImportLastSplitInfo;