mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
CsvTransImp - rework verification code to prevent new cases of invalid transactions
With the added multi-currency support it would be possible to create imbalanced splits. A new check is added to detect this beforehand and prevent users from continuing. This required much of the verification logic to be revisited.
This commit is contained in:
parent
57c525fb19
commit
625978cc33
@ -119,9 +119,6 @@ Select location and file name for the Import, then click "Next"…
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="border-width">3</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="delete_settings">
|
||||
<property name="visible">True</property>
|
||||
@ -148,6 +145,9 @@ There are two reserved names which can never be deleted:
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="save_settings">
|
||||
<property name="visible">True</property>
|
||||
@ -957,6 +957,7 @@ For example
|
||||
<object class="GtkTreeView" id="account_match_view">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="tooltip-text" translatable="yes">To change mapping, double click on a row or select a row and press the button…</property>
|
||||
<property name="model">account_match_store</property>
|
||||
<property name="enable-search">False</property>
|
||||
<property name="enable-tree-lines">True</property>
|
||||
@ -1007,6 +1008,7 @@ For example
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">Error text.</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
@ -1143,6 +1145,9 @@ More information can be displayed by using the help button.</property>
|
||||
<object class="GtkBox">
|
||||
<property name="can-focus">False</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="has-padding">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
||||
|
@ -268,6 +268,8 @@ private:
|
||||
|
||||
bool new_book; /**< Are we importing into a new book?; if yes, call book options */
|
||||
std::unique_ptr<GncTxImport> tx_imp; /**< The actual data we are previewing */
|
||||
|
||||
bool m_req_mapped_accts;
|
||||
};
|
||||
|
||||
|
||||
@ -1732,8 +1734,9 @@ CsvImpTransAssist::preview_refresh ()
|
||||
void CsvImpTransAssist::preview_validate_settings ()
|
||||
{
|
||||
/* Allow the user to proceed only if there are no inconsistencies in the settings */
|
||||
auto error_msg = tx_imp->verify();
|
||||
gtk_assistant_set_page_complete (csv_imp_asst, preview_page, error_msg.empty());
|
||||
auto has_non_acct_errors = !tx_imp->verify (false).empty();
|
||||
auto error_msg = tx_imp->verify (m_req_mapped_accts);
|
||||
gtk_assistant_set_page_complete (csv_imp_asst, preview_page, !has_non_acct_errors);
|
||||
gtk_label_set_markup(GTK_LABEL(instructions_label), error_msg.c_str());
|
||||
gtk_widget_set_visible (GTK_WIDGET(instructions_image), !error_msg.empty());
|
||||
|
||||
@ -1741,7 +1744,7 @@ void CsvImpTransAssist::preview_validate_settings ()
|
||||
* accounts in the user data according to the importer configuration
|
||||
* only if there are no errors
|
||||
*/
|
||||
if (error_msg.empty())
|
||||
if (!has_non_acct_errors)
|
||||
gtk_widget_set_visible (GTK_WIDGET(account_match_page),
|
||||
!tx_imp->accounts().empty());
|
||||
}
|
||||
@ -1852,13 +1855,33 @@ CsvImpTransAssist::acct_match_select(GtkTreeModel *model, GtkTreeIter* iter)
|
||||
// Update the account kvp mappings
|
||||
gnc_csv_account_map_change_mappings (account, gnc_acc, text);
|
||||
|
||||
// Force reparsing of account columns - may impact multi-currency mode
|
||||
auto col_types = tx_imp->column_types();
|
||||
auto col_type_it = std::find (col_types.cbegin(),
|
||||
col_types.cend(), GncTransPropType::ACCOUNT);
|
||||
if (col_type_it != col_types.cend())
|
||||
tx_imp->set_column_type(col_type_it - col_types.cbegin(),
|
||||
GncTransPropType::ACCOUNT, true);
|
||||
col_type_it = std::find (col_types.cbegin(),
|
||||
col_types.cend(), GncTransPropType::TACCOUNT);
|
||||
if (col_type_it != col_types.cend())
|
||||
tx_imp->set_column_type(col_type_it - col_types.cbegin(),
|
||||
GncTransPropType::TACCOUNT, true);
|
||||
|
||||
g_free (fullpath);
|
||||
}
|
||||
g_free (text);
|
||||
|
||||
gtk_assistant_set_page_complete (csv_imp_asst, account_match_page,
|
||||
csv_tximp_acct_match_check_all (model));
|
||||
|
||||
/* Enable the "Next" Assistant Button */
|
||||
auto all_checked = csv_tximp_acct_match_check_all (model);
|
||||
gtk_assistant_set_page_complete (csv_imp_asst, account_match_page,
|
||||
all_checked);
|
||||
|
||||
/* Update information message and whether to display account errors */
|
||||
m_req_mapped_accts = all_checked;
|
||||
auto errs = tx_imp->verify(m_req_mapped_accts);
|
||||
gtk_label_set_text (GTK_LABEL(account_match_label), errs.c_str());
|
||||
}
|
||||
|
||||
void
|
||||
@ -1944,7 +1967,7 @@ CsvImpTransAssist::assist_preview_page_prepare ()
|
||||
tx_imp->file_format (GncImpFileFormat::CSV);
|
||||
tx_imp->load_file (m_fc_file_name);
|
||||
tx_imp->tokenize (true);
|
||||
tx_imp->req_mapped_accts (false);
|
||||
m_req_mapped_accts = false;
|
||||
|
||||
/* Get settings store and populate */
|
||||
preview_populate_settings_combo();
|
||||
@ -1982,7 +2005,6 @@ CsvImpTransAssist::assist_preview_page_prepare ()
|
||||
void
|
||||
CsvImpTransAssist::assist_account_match_page_prepare ()
|
||||
{
|
||||
tx_imp->req_mapped_accts(true);
|
||||
|
||||
// Load the account strings into the store
|
||||
acct_match_set_accounts ();
|
||||
@ -1991,52 +2013,32 @@ CsvImpTransAssist::assist_account_match_page_prepare ()
|
||||
auto store = gtk_tree_view_get_model (GTK_TREE_VIEW(account_match_view));
|
||||
gnc_csv_account_map_load_mappings (store);
|
||||
|
||||
auto text = std::string ("<span size=\"medium\" color=\"red\"><b>");
|
||||
text += _("To change mapping, double click on a row or select a row and press the button…");
|
||||
text += "</b></span>";
|
||||
gtk_label_set_markup (GTK_LABEL(account_match_label), text.c_str());
|
||||
|
||||
// Enable the view, possibly after an error
|
||||
gtk_widget_set_sensitive (account_match_view, true);
|
||||
gtk_widget_set_sensitive (account_match_btn, true);
|
||||
|
||||
/* Enable the "Next" Assistant Button */
|
||||
auto all_checked = csv_tximp_acct_match_check_all (store);
|
||||
gtk_assistant_set_page_complete (csv_imp_asst, account_match_page,
|
||||
csv_tximp_acct_match_check_all (store));
|
||||
all_checked);
|
||||
|
||||
/* Update information message and whether to display account errors */
|
||||
m_req_mapped_accts = all_checked;
|
||||
auto text = tx_imp->verify (m_req_mapped_accts);
|
||||
gtk_label_set_text (GTK_LABEL(account_match_label), text.c_str());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CsvImpTransAssist::assist_doc_page_prepare ()
|
||||
{
|
||||
/* At this stage in the assistant each account should be mapped so
|
||||
* complete the split properties with this information. If this triggers
|
||||
* an exception it indicates a logic error in the code.
|
||||
*/
|
||||
try
|
||||
if (!tx_imp->verify (true).empty())
|
||||
{
|
||||
auto col_types = tx_imp->column_types();
|
||||
auto acct_col = std::find (col_types.begin(),
|
||||
col_types.end(), GncTransPropType::ACCOUNT);
|
||||
if (acct_col != col_types.end())
|
||||
tx_imp->set_column_type (acct_col - col_types.begin(),
|
||||
GncTransPropType::ACCOUNT, true);
|
||||
acct_col = std::find (col_types.begin(),
|
||||
col_types.end(), GncTransPropType::TACCOUNT);
|
||||
if (acct_col != col_types.end())
|
||||
tx_imp->set_column_type (acct_col - col_types.begin(),
|
||||
GncTransPropType::TACCOUNT, true);
|
||||
}
|
||||
catch (const std::invalid_argument& err)
|
||||
{
|
||||
/* Oops! This shouldn't happen when using the import assistant !
|
||||
* Inform the user and go back to the preview page.
|
||||
/* New accounts can change the multi-currency situation and hence
|
||||
* may require more column tweaks. If so
|
||||
* inform the user and go back to the preview page.
|
||||
*/
|
||||
gnc_error_dialog (GTK_WINDOW (csv_imp_asst),
|
||||
_("An unexpected error has occurred while mapping accounts. Please report this as a bug.\n\n"
|
||||
"Error message:\n%s"), err.what());
|
||||
gtk_assistant_set_current_page (csv_imp_asst, 2);
|
||||
|
||||
}
|
||||
|
||||
/* Block going back */
|
||||
|
@ -375,7 +375,47 @@ ErrMap GncPreTrans::errors ()
|
||||
return m_errors;
|
||||
}
|
||||
|
||||
void GncPreTrans::reset_cross_split_counters()
|
||||
{
|
||||
m_alt_currencies.clear();
|
||||
m_acct_commodities.clear();
|
||||
}
|
||||
|
||||
|
||||
bool GncPreTrans::is_multi_currency()
|
||||
{
|
||||
auto num_comm = m_acct_commodities.size() + m_alt_currencies.size();
|
||||
if (m_currency && (std::find (m_alt_currencies.cbegin(),m_alt_currencies.cend(), m_currency) == m_alt_currencies.cend()))
|
||||
num_comm++;
|
||||
return (num_comm > 1);
|
||||
}
|
||||
|
||||
|
||||
void GncPreSplit::UpdateCrossSplitCounters ()
|
||||
{
|
||||
auto acct = static_cast<Account *> (nullptr);
|
||||
if (m_account && *m_account)
|
||||
{
|
||||
auto acct = *m_account;
|
||||
auto comm = xaccAccountGetCommodity (acct);
|
||||
auto alt_currs = m_pre_trans->m_alt_currencies;
|
||||
auto acct_comms = m_pre_trans->m_acct_commodities;
|
||||
auto curr = static_cast<gnc_commodity*> (nullptr);
|
||||
if (gnc_commodity_is_currency (comm))
|
||||
{
|
||||
curr = comm;
|
||||
comm = nullptr;
|
||||
}
|
||||
else
|
||||
curr = gnc_account_get_currency_or_parent (acct);
|
||||
|
||||
auto has_curr = [curr] (const gnc_commodity *vec_curr) { return gnc_commodity_equiv (curr, vec_curr); };
|
||||
if (curr && std::none_of (alt_currs.cbegin(), alt_currs.cbegin(), has_curr))
|
||||
m_pre_trans->m_alt_currencies.push_back(curr);
|
||||
auto has_comm = [comm] (const gnc_commodity *vec_comm) { return gnc_commodity_equiv (comm, vec_comm); };
|
||||
if (comm && std::none_of (acct_comms.cbegin(), acct_comms.cbegin(), has_comm))
|
||||
m_pre_trans->m_alt_currencies.push_back(comm);
|
||||
}
|
||||
}
|
||||
|
||||
void GncPreSplit::set (GncTransPropType prop_type, const std::string& value)
|
||||
@ -500,6 +540,10 @@ void GncPreSplit::set (GncTransPropType prop_type, const std::string& value)
|
||||
e.what()).str();
|
||||
m_errors.emplace(prop_type, err_str);
|
||||
}
|
||||
|
||||
/* Extra currency related postprocessing for account type */
|
||||
if (prop_type == GncTransPropType::ACCOUNT)
|
||||
UpdateCrossSplitCounters();
|
||||
}
|
||||
|
||||
void GncPreSplit::reset (GncTransPropType prop_type)
|
||||
@ -577,6 +621,25 @@ StrVec GncPreSplit::verify_essentials()
|
||||
if (m_trec_state && *m_trec_state == YREC && !m_trec_date)
|
||||
err_msg.emplace_back (_("Transfer split is reconciled but transfer reconcile date column is missing or invalid."));
|
||||
|
||||
|
||||
/* In multisplit mode and where current account selections imply multi-
|
||||
* currency transactions, we require extra columns to ensure each split is
|
||||
* fully defined.
|
||||
* Note this check only involves splits created by the csv importer
|
||||
* code. The generic import matcher may add a balancing split
|
||||
* optionally using Transfer <something> properties. The generic
|
||||
* import matcher has its own tools to balance that split so
|
||||
* we won't concern ourselves with that one here.
|
||||
*/
|
||||
if (m_pre_trans->is_multi_currency())
|
||||
{
|
||||
if (m_pre_trans->m_multi_split && !m_price)
|
||||
err_msg.emplace_back( _("Choice of accounts makes this a multi-currency transaction but price column is missing or invalid."));
|
||||
else if (!m_pre_trans->m_multi_split &&
|
||||
!m_price && !m_tamount && !m_tamount_neg)
|
||||
err_msg.emplace_back( _("Choice of account makes this a multi-currency transaction but price or (negated) transfer column is missing or invalid."));
|
||||
}
|
||||
|
||||
return err_msg;
|
||||
}
|
||||
|
||||
@ -664,9 +727,7 @@ void GncPreSplit::create_split (std::shared_ptr<DraftTransaction> draft_trans)
|
||||
auto acct_comm = xaccAccountGetCommodity(account);
|
||||
if (gnc_commodity_equiv(trans_curr, acct_comm))
|
||||
value = amount;
|
||||
else if ((m_tamount || m_tamount_neg) &&
|
||||
m_taccount &&
|
||||
gnc_commodity_equiv (trans_curr, xaccAccountGetCommodity( *m_taccount)))
|
||||
else if (tamount)
|
||||
value = -*tamount;
|
||||
else if (m_price)
|
||||
value = amount * *m_price;
|
||||
@ -678,11 +739,11 @@ void GncPreSplit::create_split (std::shared_ptr<DraftTransaction> draft_trans)
|
||||
auto nprice =
|
||||
gnc_pricedb_lookup_nearest_in_time64(gnc_pricedb_get_db(book),
|
||||
acct_comm, trans_curr, time);
|
||||
if (nprice)
|
||||
GncNumeric rate = nprice ? gnc_price_get_value (nprice): gnc_numeric_zero();
|
||||
if (!gnc_numeric_zero_p (rate))
|
||||
{
|
||||
/* Found a usable price. Let's check if the conversion direction is right
|
||||
* Reminder: value = amount * price, or amount = value / price */
|
||||
GncNumeric rate = gnc_price_get_value(nprice);
|
||||
if (gnc_commodity_equiv(gnc_price_get_currency(nprice), trans_curr))
|
||||
value = amount * rate;
|
||||
else
|
||||
@ -719,11 +780,11 @@ void GncPreSplit::create_split (std::shared_ptr<DraftTransaction> draft_trans)
|
||||
auto nprice =
|
||||
gnc_pricedb_lookup_nearest_in_time64(gnc_pricedb_get_db(book),
|
||||
acct_comm, trans_curr, time);
|
||||
if (nprice)
|
||||
GncNumeric rate = nprice ? gnc_price_get_value (nprice): gnc_numeric_zero();
|
||||
if (!gnc_numeric_zero_p (rate))
|
||||
{
|
||||
/* Found a usable price. Let's check if the conversion direction is right
|
||||
* Reminder: value = amount * price, or amount = value / price */
|
||||
GncNumeric rate = gnc_price_get_value(nprice);
|
||||
if (gnc_commodity_equiv(gnc_price_get_currency(nprice), trans_curr))
|
||||
tamount = tvalue * rate.inv();
|
||||
else
|
||||
@ -766,3 +827,14 @@ ErrMap GncPreSplit::errors (void)
|
||||
{
|
||||
return m_errors;
|
||||
}
|
||||
|
||||
|
||||
void GncPreSplit::set_account (Account* acct)
|
||||
{
|
||||
if (acct)
|
||||
m_account = acct;
|
||||
else
|
||||
m_account = boost::none;
|
||||
|
||||
UpdateCrossSplitCounters();
|
||||
}
|
||||
|
@ -151,7 +151,7 @@ class GncPreTrans
|
||||
{
|
||||
public:
|
||||
GncPreTrans(int date_format, bool multi_split)
|
||||
: m_date_format{date_format}, m_multi_split{multi_split} {};
|
||||
: m_date_format{date_format}, m_multi_split{multi_split}, m_currency{nullptr} {};
|
||||
|
||||
void set (GncTransPropType prop_type, const std::string& value);
|
||||
void set_date_format (int date_format) { m_date_format = date_format ;}
|
||||
@ -176,7 +176,14 @@ public:
|
||||
*/
|
||||
bool is_part_of (std::shared_ptr<GncPreTrans> parent);
|
||||
boost::optional<std::string> get_void_reason() { return m_void_reason; }
|
||||
|
||||
ErrMap errors();
|
||||
void reset_cross_split_counters();
|
||||
/* Some import errors need info from multiple splits. This function
|
||||
* will evaluate possible multi-line errors and set the proper error
|
||||
* message(s) for them. */
|
||||
bool is_multi_currency();
|
||||
|
||||
|
||||
private:
|
||||
int m_date_format;
|
||||
@ -190,7 +197,21 @@ private:
|
||||
boost::optional<std::string> m_void_reason;
|
||||
bool created = false;
|
||||
|
||||
|
||||
ErrMap m_errors;
|
||||
|
||||
/* m_alt_currencies will be filled with all PreSplit account's
|
||||
* commodities that are currencies. If the account is denominated in a
|
||||
* non-currency, its parent account currency is added instead.
|
||||
* This list will be used to check for multi-currency inconsistencies
|
||||
* and whether extra columns are required. */
|
||||
std::vector<gnc_commodity*> m_alt_currencies;
|
||||
/* m_acct_commodities will be filled with all PreSplit account's
|
||||
* commodities that aren't currencies. The result will be used to check for
|
||||
* a multi-currency situation (which requires extra columns to be set). */
|
||||
std::vector<gnc_commodity*> m_acct_commodities;
|
||||
|
||||
friend class GncPreSplit;
|
||||
};
|
||||
|
||||
class GncPreSplit
|
||||
@ -209,10 +230,12 @@ public:
|
||||
void create_split(std::shared_ptr<DraftTransaction> draft_trans);
|
||||
|
||||
Account* get_account () { if (m_account) return *m_account; else return nullptr; }
|
||||
void set_account (Account* acct) { if (acct) m_account = acct; else m_account = boost::none; }
|
||||
void set_account (Account* acct);
|
||||
ErrMap errors();
|
||||
|
||||
private:
|
||||
void UpdateCrossSplitCounters ();
|
||||
|
||||
std::shared_ptr<GncPreTrans> m_pre_trans;
|
||||
int m_date_format;
|
||||
int m_currency_format;
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <numeric>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
@ -65,7 +66,6 @@ GncTxImport::GncTxImport(GncImpFileFormat format)
|
||||
* gnc_csv_parse_data_free is called before all of the data is
|
||||
* initialized, only the data that needs to be freed is freed. */
|
||||
m_skip_errors = false;
|
||||
m_req_mapped_accts = true;
|
||||
file_format(m_settings.m_file_format = format);
|
||||
}
|
||||
|
||||
@ -447,19 +447,26 @@ struct ErrorList
|
||||
public:
|
||||
void add_error (std::string msg);
|
||||
std::string str();
|
||||
bool empty() { return m_error.empty(); }
|
||||
private:
|
||||
std::string m_error;
|
||||
StrVec m_error;
|
||||
};
|
||||
|
||||
void ErrorList::add_error (std::string msg)
|
||||
{
|
||||
m_error += "- " + msg + "\n";
|
||||
m_error.emplace_back (msg);
|
||||
}
|
||||
|
||||
std::string ErrorList::str()
|
||||
{
|
||||
return m_error.substr(0, m_error.size() - 1);
|
||||
auto err_msg = std::string();
|
||||
if (!m_error.empty())
|
||||
{
|
||||
auto add_bullet_item = [](std::string& a, std::string& b)->std::string { return std::move(a) + "\n• " + b; };
|
||||
err_msg = std::accumulate (m_error.begin(), m_error.end(), std::move (err_msg), add_bullet_item);
|
||||
err_msg.erase (0, 1);
|
||||
}
|
||||
|
||||
return err_msg;
|
||||
}
|
||||
|
||||
|
||||
@ -495,7 +502,7 @@ void GncTxImport::verify_column_selections (ErrorList& error_msg)
|
||||
*/
|
||||
if (!check_for_column_type(GncTransPropType::AMOUNT) &&
|
||||
!check_for_column_type(GncTransPropType::AMOUNT_NEG))
|
||||
error_msg.add_error( _("Please select a amount or negated amount column."));
|
||||
error_msg.add_error( _("Please select a (negated) amount column."));
|
||||
|
||||
/* Verify a transfer account is selected if any of the other transfer properties
|
||||
* are selected.
|
||||
@ -506,6 +513,26 @@ void GncTxImport::verify_column_selections (ErrorList& error_msg)
|
||||
check_for_column_type(GncTransPropType::TREC_DATE)) &&
|
||||
!check_for_column_type(GncTransPropType::TACCOUNT))
|
||||
error_msg.add_error( _("Please select a transfer account column or remove the other transfer related columns."));
|
||||
|
||||
/* In multisplit mode and where current account selections imply multi-
|
||||
* currency transactions, we require extra columns to ensure each split is
|
||||
* fully defined.
|
||||
* Note this check only involves splits created by the csv importer
|
||||
* code. The generic import matcher may add a balancing split
|
||||
* optionally using Transfer <something> properties. The generic
|
||||
* import matcher has its own tools to balance that split so
|
||||
* we won't concern ourselves with that one here.
|
||||
*/
|
||||
if (m_multi_currency)
|
||||
{
|
||||
if (m_settings.m_multi_split && !check_for_column_type(GncTransPropType::PRICE))
|
||||
error_msg.add_error( _("The current account selections will generate multi-currency transactions. Please select a price column."));
|
||||
else if (!m_settings.m_multi_split &&
|
||||
!check_for_column_type(GncTransPropType::PRICE) &&
|
||||
!check_for_column_type(GncTransPropType::TAMOUNT) &&
|
||||
!check_for_column_type(GncTransPropType::TAMOUNT_NEG))
|
||||
error_msg.add_error( _("The current account selections will generate multi-currency transactions. Please select a price column or a (negated) transfer column."));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -517,7 +544,7 @@ void GncTxImport::verify_column_selections (ErrorList& error_msg)
|
||||
* @return An empty string if all checks passed or the reason
|
||||
* verification failed otherwise.
|
||||
*/
|
||||
std::string GncTxImport::verify ()
|
||||
std::string GncTxImport::verify (bool with_acct_errors)
|
||||
{
|
||||
auto newline = std::string();
|
||||
auto error_msg = ErrorList();
|
||||
@ -544,7 +571,21 @@ std::string GncTxImport::verify ()
|
||||
auto have_line_errors = false;
|
||||
for (auto line : m_parsed_lines)
|
||||
{
|
||||
if (!std::get<PL_SKIP>(line) && !std::get<PL_ERROR>(line).empty())
|
||||
auto errors = std::get<PL_ERROR>(line);
|
||||
if (std::get<PL_SKIP>(line))
|
||||
continue;
|
||||
if (with_acct_errors && !errors.empty())
|
||||
{
|
||||
have_line_errors = true;
|
||||
break;
|
||||
}
|
||||
auto non_acct_error = [with_acct_errors](ErrPair curr_err)
|
||||
{
|
||||
return !((curr_err.first == GncTransPropType::ACCOUNT) ||
|
||||
(curr_err.first == GncTransPropType::TACCOUNT));
|
||||
};
|
||||
if (!with_acct_errors &&
|
||||
std::any_of(errors.cbegin(), errors.cend(), non_acct_error))
|
||||
{
|
||||
have_line_errors = true;
|
||||
break;
|
||||
@ -575,7 +616,7 @@ std::shared_ptr<DraftTransaction> GncTxImport::trans_properties_to_trans (std::v
|
||||
QofBook* book = gnc_account_get_book (account);
|
||||
gnc_commodity* currency = xaccAccountGetCommodity (account);
|
||||
if (!gnc_commodity_is_currency(currency))
|
||||
currency = xaccAccountGetCommodity (gnc_account_get_parent (account));
|
||||
currency = gnc_account_get_currency_or_parent (account);
|
||||
|
||||
auto draft_trans = trans_props->create_trans (book, currency);
|
||||
|
||||
@ -679,7 +720,7 @@ void GncTxImport::create_transaction (std::vector<parse_line_t>::iterator& parse
|
||||
void GncTxImport::create_transactions ()
|
||||
{
|
||||
/* Start with verifying the current data. */
|
||||
auto verify_result = verify();
|
||||
auto verify_result = verify (true);
|
||||
if (!verify_result.empty())
|
||||
throw std::invalid_argument (verify_result);
|
||||
|
||||
@ -735,6 +776,12 @@ void GncTxImport::update_pre_trans_split_props (uint32_t row, uint32_t col, GncT
|
||||
trans_props->set(new_type, value);
|
||||
}
|
||||
|
||||
/* In the trans_props we also keep track of currencies/commodities for further
|
||||
* multi-currency checks. These come from a PreSplit's account property.
|
||||
* If that's the property that we are about to modify, the current
|
||||
* counters should be reset. */
|
||||
if ((old_type == GncTransPropType::ACCOUNT) || (new_type == GncTransPropType::ACCOUNT))
|
||||
trans_props->reset_cross_split_counters();
|
||||
|
||||
/* All transaction related value updates are finished now,
|
||||
* time to determine what to do with the updated GncPreTrans copy.
|
||||
@ -746,7 +793,7 @@ void GncTxImport::update_pre_trans_split_props (uint32_t row, uint32_t col, GncT
|
||||
* In all other cases our new GncPreTrans should be used for this line
|
||||
* and be marked as the new potential m_parent for subsequent lines.
|
||||
*/
|
||||
if (m_settings.m_multi_split && trans_props->is_part_of(m_parent))
|
||||
if (m_settings.m_multi_split && trans_props->is_part_of( m_parent))
|
||||
split_props->set_pre_trans (m_parent);
|
||||
else
|
||||
{
|
||||
@ -754,7 +801,7 @@ void GncTxImport::update_pre_trans_split_props (uint32_t row, uint32_t col, GncT
|
||||
m_parent = trans_props;
|
||||
}
|
||||
|
||||
/* Next handle any split related property changes */
|
||||
/* Finally handle any split related property changes */
|
||||
if ((old_type > GncTransPropType::TRANS_PROPS) && (old_type <= GncTransPropType::SPLIT_PROPS))
|
||||
{
|
||||
split_props->reset (old_type);
|
||||
@ -800,6 +847,7 @@ void GncTxImport::update_pre_trans_split_props (uint32_t row, uint32_t col, GncT
|
||||
split_props->set(new_type, value);
|
||||
}
|
||||
}
|
||||
m_multi_currency |= split_props->get_pre_trans()->is_multi_currency();
|
||||
}
|
||||
|
||||
|
||||
@ -827,6 +875,7 @@ GncTxImport::set_column_type (uint32_t position, GncTransPropType type, bool for
|
||||
|
||||
/* Update the preparsed data */
|
||||
m_parent = nullptr;
|
||||
m_multi_currency = false;
|
||||
for (auto parsed_lines_it = m_parsed_lines.begin();
|
||||
parsed_lines_it != m_parsed_lines.end();
|
||||
++parsed_lines_it)
|
||||
|
@ -128,8 +128,6 @@ public:
|
||||
bool skip_alt_lines ();
|
||||
bool skip_err_lines ();
|
||||
|
||||
void req_mapped_accts (bool val) {m_req_mapped_accts = val; }
|
||||
|
||||
void separators (std::string separators);
|
||||
std::string separators ();
|
||||
|
||||
@ -144,7 +142,7 @@ public:
|
||||
|
||||
void tokenize (bool guessColTypes);
|
||||
|
||||
std::string verify();
|
||||
std::string verify(bool with_acct_errors);
|
||||
|
||||
/** This function will attempt to convert all tokenized lines into
|
||||
* transactions using the column types the user has set.
|
||||
@ -187,7 +185,8 @@ private:
|
||||
|
||||
CsvTransImpSettings m_settings;
|
||||
bool m_skip_errors;
|
||||
bool m_req_mapped_accts;
|
||||
/* Field used internally to track whether some transactions are multi-currency */
|
||||
bool m_multi_currency;
|
||||
|
||||
/* The parameters below are only used while creating
|
||||
* transactions. They keep state information while processing multi-split
|
||||
|
Loading…
Reference in New Issue
Block a user