Example #1
0
/** Adds a split to a transaction.
 * @param trans The transaction to add a split to
 * @param account The split's account
 * @param amount The split's amount
 * @param rec_state The split's reconcile status
 * @param rec_date The split's reconcile date
 * @param price The split's conversion rate from account commodity to transaction commodity
 */
static void trans_add_split (Transaction* trans, Account* account, GncNumeric amount,
                            const boost::optional<std::string>& action,
                            const boost::optional<std::string>& memo,
                            const boost::optional<char>& rec_state,
                            const boost::optional<GncDate>& rec_date,
                            const boost::optional<GncNumeric> price)
{
    QofBook* book = xaccTransGetBook (trans);
    auto split = xaccMallocSplit (book);
    xaccSplitSetAccount (split, account);
    xaccSplitSetParent (split, trans);
    xaccSplitSetAmount (split, static_cast<gnc_numeric>(amount));
    auto trans_curr = xaccTransGetCurrency(trans);
    auto acct_comm = xaccAccountGetCommodity(account);
    GncNumeric value;
    if (gnc_commodity_equiv(trans_curr, acct_comm))
        value = amount;
    else if (price)
        value = amount * *price;
    else
    {
        auto time = xaccTransRetDatePosted (trans);
        /* Import data didn't specify price, let's lookup the nearest in time */
        auto nprice =
            gnc_pricedb_lookup_nearest_in_time64(gnc_pricedb_get_db(book),
                                                 acct_comm, trans_curr, time);
        if (nprice)
        {
            /* Found a usable price. Let's check if the conversion direction is right */
            GncNumeric rate;
            if (gnc_commodity_equiv(gnc_price_get_currency(nprice), trans_curr))
                rate = gnc_price_get_value(nprice);
            else
                rate = static_cast<GncNumeric>(gnc_price_get_value(nprice)).inv();

            value = amount * rate;
        }
        else
        {
            PWARN("No price found, using a price of 1.");
            value = amount;
        }
    }
    xaccSplitSetValue (split, static_cast<gnc_numeric>(value));

    if (memo)
        xaccSplitSetMemo (split, memo->c_str());
    /* Note, this function assumes the num/action switch is done at a higher level
     * if needed by the book option */
    if (action)
        xaccSplitSetAction (split, action->c_str());

    if (rec_state && *rec_state != 'n')
        xaccSplitSetReconcile (split, *rec_state);
    if (rec_state && *rec_state == YREC && rec_date)
        xaccSplitSetDateReconciledSecs (split,
                static_cast<time64>(GncDateTime(*rec_date, DayPart::neutral)));

}
Example #2
0
/*
 * Given an owner, extract the open balance from the owner and then
 * convert it to the desired currency.
 */
gnc_numeric
gncOwnerGetBalanceInCurrency (const GncOwner *owner,
                              const gnc_commodity *report_currency)
{
    gnc_numeric balance = gnc_numeric_zero ();
    GList *acct_list, *acct_node, *acct_types, *lot_list = NULL, *lot_node;
    QofBook *book;
    gnc_commodity *owner_currency;
    GNCPriceDB *pdb;

    g_return_val_if_fail (owner, gnc_numeric_zero ());

    /* Get account list */
    book       = qof_instance_get_book (qofOwnerGetOwner (owner));
    acct_list  = gnc_account_get_descendants (gnc_book_get_root_account (book));
    acct_types = gncOwnerGetAccountTypesList (owner);
    owner_currency = gncOwnerGetCurrency (owner);

    /* For each account */
    for (acct_node = acct_list; acct_node; acct_node = acct_node->next)
    {
        Account *account = acct_node->data;

        /* Check if this account can have lots for the owner, otherwise skip to next */
        if (g_list_index (acct_types, (gpointer)xaccAccountGetType (account))
                == -1)
            continue;


        if (!gnc_commodity_equal (owner_currency, xaccAccountGetCommodity (account)))
            continue;

        /* Get a list of open lots for this owner and account */
        lot_list = xaccAccountFindOpenLots (account, gncOwnerLotMatchOwnerFunc,
                                            (gpointer)owner, NULL);
        /* For each lot */
        for (lot_node = lot_list; lot_node; lot_node = lot_node->next)
        {
            GNCLot *lot = lot_node->data;
            gnc_numeric lot_balance = gnc_lot_get_balance (lot);
            GncInvoice *invoice = gncInvoiceGetInvoiceFromLot(lot);
            if (invoice)
               balance = gnc_numeric_add (balance, lot_balance,
                                          gnc_commodity_get_fraction (owner_currency), GNC_HOW_RND_ROUND_HALF_UP);
        }
    }

    pdb = gnc_pricedb_get_db (book);

    if (report_currency)
        balance = gnc_pricedb_convert_balance_latest_price (
                      pdb, balance, owner_currency, report_currency);

    return balance;
}
static gboolean
test_add_pricedb (const char* tag, gpointer globaldata, gpointer data)
{
    sixtp_gdv2* gdata = static_cast<decltype (gdata)> (globaldata);
    GNCPriceDB* db1 = static_cast<decltype (db1)> (data);
    GNCPriceDB* db2 = gnc_pricedb_get_db (gdata->book);

    do_test_args (gnc_pricedb_equal (db1, db2),
                  "gnc_pricedb_sixtp_parser_create",
                  __FILE__, __LINE__, "%d", iter);

    return TRUE;
}
Example #4
0
static gboolean
write_prices (GncSqlBackend* be)
{
    GNCPriceDB* priceDB;
    write_objects_t data;

    g_return_val_if_fail (be != NULL, FALSE);

    priceDB = gnc_pricedb_get_db (be->book);

    data.be = be;
    data.is_ok = TRUE;
    return gnc_pricedb_foreach_price (priceDB, write_price, &data, TRUE);
}
Example #5
0
/* Get the rate from the price db */
static gnc_numeric
gtu_sr_get_rate_from_db (gnc_commodity *from, gnc_commodity *to)
{
    GNCPrice *prc;
    gnc_numeric rate_split;
    gboolean have_rate = FALSE;
    QofBook *book = gnc_get_current_book ();

    /* Do we have a rate allready */
    prc = gnc_pricedb_lookup_latest (gnc_pricedb_get_db (book), from, to);
    if (prc)
    {
        rate_split = gnc_price_get_value (prc);
        gnc_price_unref (prc);
        have_rate = TRUE;
    }

    /* Lets try reversing the commodities */
    if (!have_rate)
    {
        prc = gnc_pricedb_lookup_latest (gnc_pricedb_get_db (book), to, from);
        if (prc)
        {
            rate_split = gnc_numeric_div (gnc_numeric_create (100, 100), gnc_price_get_value (prc),
                                 GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE);

            gnc_price_unref (prc);
            have_rate = TRUE;
        }
    }

    /* No rate, set to 1/1 */
    if (!have_rate)
        rate_split = gnc_numeric_create (100, 100);

    return rate_split;
}
/* Get the rate from the price db */
static gnc_numeric
gtu_sr_get_rate_from_db (gnc_commodity *from, gnc_commodity *to)
{
    GNCPrice *prc;
    QofBook *book = gnc_get_current_book ();

    prc = gnc_pricedb_lookup_latest (gnc_pricedb_get_db (book), from, to);

    if (!prc)
        return gnc_numeric_create (100, 100);

    if (gnc_commodity_equiv(from, gnc_price_get_currency(prc)))
        return gnc_numeric_invert(gnc_price_get_value(prc));
    return gnc_price_get_value(prc);
}
Example #7
0
xmlNodePtr
gnc_book_dom_tree_create(QofBook *book)
{
    xmlNodePtr ret;
    G_GNUC_UNUSED gboolean allow_incompat = TRUE;

    ret = xmlNewNode(NULL, BAD_CAST gnc_book_string);
    xmlSetProp(ret, BAD_CAST "version", BAD_CAST gnc_v2_book_version_string);

    xmlAddChild(ret, guid_to_dom_tree(book_id_string,
                                      qof_book_get_guid(book)));

    if (qof_instance_get_slots (QOF_INSTANCE (book)))
    {
        xmlNodePtr kvpnode = kvp_frame_to_dom_tree(book_slots_string,
                             qof_instance_get_slots (QOF_INSTANCE (book)));
        if (kvpnode)
            xmlAddChild(ret, kvpnode);
    }

#ifdef IMPLEMENT_BOOK_DOM_TREES_LATER
    /* theoretically, we should be adding all the below to the book
     * but in fact, there's enough brain damage in the code already
     * that we are only going to hand-edit the file at a higher layer.
     * And that's OK, since its probably a performance boost anyway.
     */
    xmlAddChild(ret, gnc_commodity_dom_tree_create(
                    gnc_commodity_table_get_table(book)));
    xmlAddChild(ret, gnc_pricedb_dom_tree_create(gnc_pricedb_get_db(book)));
    if (allow_incompat)
    {
        accnode = gnc_account_dom_tree_create(account, FALSE);
        xmlAddChild (ret, rootAccNode);
    }
    append_account_tree (ret, gnc_book_get_root(book));

    xaccAccountTreeForEachTransaction (gnc_book_get_root_account(book),
                                       traverse_txns, ret);

    /* xxx FIXME hack alert how are we going to handle
     *  gnc_book_get_template_group handled ???   */
    xmlAddChild(ret, gnc_schedXaction_dom_tree_create(
                    gnc_book_get_schedxactions(book)));

#endif

    return ret;
}
static gboolean
pricedb_start_handler(GSList* sibling_data,
                      gpointer parent_data,
                      gpointer global_data,
                      gpointer *data_for_children,
                      gpointer *result,
                      const gchar *tag,
                      gchar **attrs)
{
    gxpf_data *gdata = global_data;
    QofBook *book = gdata->bookdata;
    GNCPriceDB *db = gnc_pricedb_get_db(book);
    g_return_val_if_fail(db, FALSE);
    gnc_pricedb_set_bulk_update(db, TRUE);
    *result = db;
    return(TRUE);
}
static void
refresh_details_page (StockSplitInfo *info)
{
    GNCPrintAmountInfo print_info;
    gnc_commodity *commodity, *currency;
    Account *account;
    QofBook *book;
    GNCPriceDB *db;
    GList *prices;

    account = info->acct;

    g_return_if_fail (account != NULL);

    print_info = gnc_account_print_info (account, FALSE);

    gnc_amount_edit_set_print_info (GNC_AMOUNT_EDIT (info->distribution_edit),
                                    print_info);
    gnc_amount_edit_set_fraction (GNC_AMOUNT_EDIT (info->distribution_edit),
                                  xaccAccountGetCommoditySCU (account));

    commodity = xaccAccountGetCommodity (account);
    book = gnc_account_get_book (account);
    db = gnc_pricedb_get_db(book);

    prices = gnc_pricedb_lookup_latest_any_currency(db, commodity);
    if (prices)
    {
        /* Use the first existing price */
        if (gnc_commodity_equiv (commodity, gnc_price_get_currency(prices->data)))
            currency = gnc_price_get_commodity(prices->data);
        else
            currency = gnc_price_get_currency(prices->data);
    }
    else
    {
        /* Take a wild guess. */
        currency = gnc_default_currency ();
    }
    gnc_price_list_destroy(prices);

    gnc_currency_edit_set_currency
    (GNC_CURRENCY_EDIT (info->price_currency_edit),
     currency);
}
Example #10
0
static void
load_all_prices (GncSqlBackend* be)
{
    GncSqlStatement* stmt;
    GncSqlResult* result;
    QofBook* pBook;
    GNCPriceDB* pPriceDB;

    g_return_if_fail (be != NULL);

    pBook = be->book;
    pPriceDB = gnc_pricedb_get_db (pBook);
    stmt = gnc_sql_create_select_statement (be, TABLE_NAME);
    if (stmt != NULL)
    {
        result = gnc_sql_execute_select_statement (be, stmt);
        gnc_sql_statement_dispose (stmt);
        if (result != NULL)
        {
            GNCPrice* pPrice;
            GncSqlRow* row = gnc_sql_result_get_first_row (result);
            gchar* sql;

            gnc_pricedb_set_bulk_update (pPriceDB, TRUE);
            while (row != NULL)
            {
                pPrice = load_single_price (be, row);

                if (pPrice != NULL)
                {
                    (void)gnc_pricedb_add_price (pPriceDB, pPrice);
                    gnc_price_unref (pPrice);
                }
                row = gnc_sql_result_get_next_row (result);
            }
            gnc_sql_result_dispose (result);
            gnc_pricedb_set_bulk_update (pPriceDB, FALSE);

            sql = g_strdup_printf ("SELECT DISTINCT guid FROM %s", TABLE_NAME);
            gnc_sql_slots_load_for_sql_subquery (be, sql, (BookLookupFn)gnc_price_lookup);
            g_free (sql);
        }
    }
}
void
gnc_stock_split_assistant_finish (GtkAssistant *assistant,
                                  gpointer user_data)
{
    StockSplitInfo *info = user_data;
    GList *account_commits;
    GList *node;

    gnc_numeric amount;
    Transaction *trans;
    Account *account;
    Split *split;
    time64 date;

    account = info->acct;
    g_return_if_fail (account != NULL);

    amount = gnc_amount_edit_get_amount
             (GNC_AMOUNT_EDIT (info->distribution_edit));
    g_return_if_fail (!gnc_numeric_zero_p (amount));

    gnc_suspend_gui_refresh ();

    trans = xaccMallocTransaction (gnc_get_current_book ());

    xaccTransBeginEdit (trans);

    xaccTransSetCurrency (trans, gnc_default_currency ());

    date = gnc_date_edit_get_date (GNC_DATE_EDIT (info->date_edit));
    xaccTransSetDatePostedSecsNormalized (trans, date);

    {
        const char *description;

        description = gtk_entry_get_text (GTK_ENTRY (info->description_entry));
        xaccTransSetDescription (trans, description);
    }

    split = xaccMallocSplit (gnc_get_current_book ());

    xaccAccountBeginEdit (account);
    account_commits = g_list_prepend (NULL, account);

    xaccTransAppendSplit (trans, split);

    xaccAccountInsertSplit (account, split);

    xaccSplitSetAmount (split, amount);
    xaccSplitMakeStockSplit (split);
    /* Set split-action with gnc_set_num_action which is the same as
     * xaccSplitSetAction with these arguments */
    /* Translators: This string has a disambiguation prefix */
    gnc_set_num_action (NULL, split, NULL, Q_("Action Column|Split"));

    amount = gnc_amount_edit_get_amount (GNC_AMOUNT_EDIT (info->price_edit));
    if (gnc_numeric_positive_p (amount))
    {
        QofBook *book;
        GNCPrice *price;
        GNCPriceDB *pdb;
        GNCCurrencyEdit *ce;
        Timespec ts;

        ce = GNC_CURRENCY_EDIT (info->price_currency_edit);

        ts.tv_sec = date;
        ts.tv_nsec = 0;

        price = gnc_price_create (gnc_get_current_book ());

        gnc_price_begin_edit (price);
        gnc_price_set_commodity (price, xaccAccountGetCommodity (account));
        gnc_price_set_currency (price, gnc_currency_edit_get_currency (ce));
        gnc_price_set_time (price, ts);
        gnc_price_set_source (price, PRICE_SOURCE_STOCK_SPLIT);
        gnc_price_set_typestr (price, PRICE_TYPE_UNK);
        gnc_price_set_value (price, amount);
        gnc_price_commit_edit (price);

        book = gnc_get_current_book ();
        pdb = gnc_pricedb_get_db (book);

        if (!gnc_pricedb_add_price (pdb, price))
            gnc_error_dialog (info->window, "%s", _("Error adding price."));

    }

    amount = gnc_amount_edit_get_amount (GNC_AMOUNT_EDIT (info->cash_edit));
    if (gnc_numeric_positive_p (amount))
    {
        const char *memo;

        memo = gtk_entry_get_text (GTK_ENTRY (info->memo_entry));

        /* asset split */
        account = gnc_tree_view_account_get_selected_account (GNC_TREE_VIEW_ACCOUNT(info->asset_tree));

        split = xaccMallocSplit (gnc_get_current_book ());

        xaccAccountBeginEdit (account);
        account_commits = g_list_prepend (account_commits, account);

        xaccAccountInsertSplit (account, split);

        xaccTransAppendSplit (trans, split);

        xaccSplitSetAmount (split, amount);
        xaccSplitSetValue (split, amount);

        xaccSplitSetMemo (split, memo);


        /* income split */
        account = gnc_tree_view_account_get_selected_account (GNC_TREE_VIEW_ACCOUNT(info->income_tree));

        split = xaccMallocSplit (gnc_get_current_book ());

        xaccAccountBeginEdit (account);
        account_commits = g_list_prepend (account_commits, account);

        xaccAccountInsertSplit (account, split);

        xaccTransAppendSplit (trans, split);

        xaccSplitSetAmount (split, gnc_numeric_neg (amount));
        xaccSplitSetValue (split, gnc_numeric_neg (amount));

        xaccSplitSetMemo (split, memo);
    }

    xaccTransCommitEdit (trans);

    for (node = account_commits; node; node = node->next)
        xaccAccountCommitEdit (node->data);
    g_list_free (account_commits);

    gnc_resume_gui_refresh ();

    gnc_close_gui_component_by_data (ASSISTANT_STOCK_SPLIT_CM_CLASS, info);
}
Example #12
0
/*
 * Create a new price tree view with (optional) top level root node.
 * This view will be based on a model that is common to all view of
 * the same set of books, but will have its own private filter on that
 * model.
 */
GtkTreeView *
gnc_tree_view_price_new (QofBook *book,
                         const gchar *first_property_name,
                         ...)
{
    GncTreeView *view;
    GtkTreeModel *model, *f_model, *s_model;
    GtkTreeViewColumn *col;
    GNCPriceDB *price_db;
    va_list var_args;
    const gchar *sample_text;
    gchar *sample_text2;

    ENTER(" ");
    /* Create/get a pointer to the existing model for this set of books. */
    price_db = gnc_pricedb_get_db(book);
    model = gnc_tree_model_price_new (book, price_db);

    /* Set up the view private filter on the common model. */
    f_model = gtk_tree_model_filter_new (model, NULL);
    g_object_unref(G_OBJECT(model));
    s_model = gtk_tree_model_sort_new_with_model (f_model);
    g_object_unref(G_OBJECT(f_model));

    /* Create our view */
    view = g_object_new (GNC_TYPE_TREE_VIEW_PRICE,
                         "name", "price_tree", NULL);
    gtk_tree_view_set_model (GTK_TREE_VIEW (view), s_model);
    g_object_unref(G_OBJECT(s_model));

    DEBUG("model ref count is %d",   G_OBJECT(model)->ref_count);
    DEBUG("f_model ref count is %d", G_OBJECT(f_model)->ref_count);
    DEBUG("s_model ref count is %d", G_OBJECT(s_model)->ref_count);

    sample_text = gnc_commodity_get_printname(gnc_default_currency());
    sample_text2 = g_strdup_printf("%s%s", sample_text, sample_text);
    col = gnc_tree_view_add_text_column (
              view, _("Security"), "security", NULL, sample_text2,
              GNC_TREE_MODEL_PRICE_COL_COMMODITY,
              GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
              sort_by_name);
    g_free(sample_text2);
    col = gnc_tree_view_add_text_column (
              view, _("Currency"), "currency", NULL, sample_text,
              GNC_TREE_MODEL_PRICE_COL_CURRENCY,
              GNC_TREE_MODEL_PRICE_COL_VISIBILITY,
              sort_by_name);
    g_object_set_data(G_OBJECT(col), DEFAULT_VISIBLE, GINT_TO_POINTER(1));
    col = gnc_tree_view_add_text_column (
              view, _("Date"), "date", NULL, "2005-05-20",
              GNC_TREE_MODEL_PRICE_COL_DATE,
              GNC_TREE_MODEL_PRICE_COL_VISIBILITY,
              sort_by_date);
    g_object_set_data(G_OBJECT(col), DEFAULT_VISIBLE, GINT_TO_POINTER(1));
    col = gnc_tree_view_add_text_column (
              view, _("Source"), "source", NULL, "Finance::Quote",
              GNC_TREE_MODEL_PRICE_COL_SOURCE,
              GNC_TREE_MODEL_PRICE_COL_VISIBILITY,
              sort_by_source);
    g_object_set_data(G_OBJECT(col), DEFAULT_VISIBLE, GINT_TO_POINTER(1));
    col = gnc_tree_view_add_text_column (
              view, _("Type"), "type", NULL, "last",
              GNC_TREE_MODEL_PRICE_COL_TYPE,
              GNC_TREE_MODEL_PRICE_COL_VISIBILITY,
              sort_by_type);
    g_object_set_data(G_OBJECT(col), DEFAULT_VISIBLE, GINT_TO_POINTER(1));
    col = gnc_tree_view_add_numeric_column (
              view, _("Price"), "price", "100.00000",
              GNC_TREE_MODEL_PRICE_COL_VALUE,
              GNC_TREE_VIEW_COLUMN_COLOR_NONE,
              GNC_TREE_MODEL_PRICE_COL_VISIBILITY,
              sort_by_value);
    g_object_set_data(G_OBJECT(col), DEFAULT_VISIBLE, GINT_TO_POINTER(1));

    gnc_tree_view_configure_columns(view);

    /* Set properties */
    va_start (var_args, first_property_name);
    g_object_set_valist (G_OBJECT(view), first_property_name, var_args);
    va_end (var_args);

    /* Sort on the commodity column by default. This allows for a consistent
     * sort if commodities are removed and re-added from the model. */
    if (!gtk_tree_sortable_get_sort_column_id(GTK_TREE_SORTABLE(s_model),
            NULL, NULL))
    {
        gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(s_model),
                                             GNC_TREE_MODEL_PRICE_COL_COMMODITY,
                                             GTK_SORT_ASCENDING);
    }

    gtk_widget_show(GTK_WIDGET(view));
    LEAVE(" %p", view);
    return GTK_TREE_VIEW(view);
}
static void
remove_clicked (CommoditiesDialog *cd)
{
    GNCPriceDB *pdb;
    GList *prices;
    gboolean can_delete;
    gnc_commodity *commodity;
    GtkWidget *dialog;
    const gchar *message, *warning;
    gint response;

    commodity = gnc_tree_view_commodity_get_selected_commodity (cd->commodity_tree);
    if (commodity == NULL)
        return;

    AccountList_t accounts = gnc_account_get_descendants (gnc_book_get_root_account(cd->book));
    can_delete = TRUE;

    for (AccountList_t::const_iterator node = accounts.begin(); 
            node != accounts.end(); node++)
    {
        Account *account = *node;

        if (commodity == xaccAccountGetCommodity (account))
        {
            can_delete = FALSE;
            break;
        }
    }

    /* FIXME check for transaction references */

    if (!can_delete)
    {
        const char *message = _("That commodity is currently used by "
                                "at least one of your accounts. You may "
                                "not delete it.");

        gnc_warning_dialog (cd->dialog, "%s", message);
        return;
    }

    pdb = gnc_pricedb_get_db (cd->book);
    prices = gnc_pricedb_get_prices(pdb, commodity, NULL);
    if (prices)
    {
        message = _("This commodity has price quotes. Are "
                    "you sure you want to delete the selected "
                    "commodity and its price quotes?");
        warning = "delete_commodity2";
    }
    else
    {
        message = _("Are you sure you want to delete the "
                    "selected commodity?");
        warning = "delete_commodity";
    }

    dialog = gtk_message_dialog_new(GTK_WINDOW(cd->dialog),
                                    GTK_DIALOG_DESTROY_WITH_PARENT,
                                    GTK_MESSAGE_QUESTION,
                                    GTK_BUTTONS_NONE,
                                    "%s", _("Delete commodity?"));
    gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
            "%s", message);
    gtk_dialog_add_buttons(GTK_DIALOG(dialog),
                           GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                           GTK_STOCK_DELETE, GTK_RESPONSE_OK,
                           (gchar *)NULL);
    response = gnc_dialog_run(GTK_DIALOG(dialog), warning);
    gtk_widget_destroy(dialog);

    if (response == GTK_RESPONSE_OK)
    {
        gnc_commodity_table *ct;

        ct = gnc_commodity_table_get_table (cd->book);
        for (GList *node = prices; node; node = node->next)
            gnc_pricedb_remove_price(pdb, node->data);

        gnc_commodity_table_remove (ct, commodity);
        gnc_commodity_destroy (commodity);
        commodity = NULL;
    }

    gnc_price_list_destroy(prices);
    gnc_gui_refresh_all ();
}
Example #14
0
void GncPriceImport::create_price (std::vector<parse_line_t>::iterator& parsed_line)
{
    StrVec line;
    std::string error_message;
    std::shared_ptr<GncImportPrice> price_props = nullptr;
    bool skip_line = false;
    std::tie(line, error_message, price_props, skip_line) = *parsed_line;

    if (skip_line)
        return;

    error_message.clear();

    // Add a TO_CURRENCY property with the selected 'currency to' if no 'currency to' column was set by the user
    auto line_to_currency = price_props->get_to_currency();
    if (!line_to_currency)
    {
        if (m_settings.m_to_currency)
            price_props->set_to_currency(m_settings.m_to_currency);
        else
        {
            // Oops - the user didn't select a 'currency to' column *and* we didn't get a selected value either!
            // Note if you get here this suggests a bug in the code!
            error_message = _("No 'Currency to' column selected and no selected Currency specified either.\n"
                                       "This should never happen. Please report this as a bug.");
            PINFO("User warning: %s", error_message.c_str());
            throw std::invalid_argument(error_message);
        }
    }

    // Add a FROM_COMMODITY property with the selected 'commodity from' if no 'commodity from' column was set by the user
    auto line_from_commodity = price_props->get_from_commodity();
    if (!line_from_commodity)
    {
        if (m_settings.m_from_commodity)
            price_props->set_from_commodity(m_settings.m_from_commodity);
        else
        {
            // Oops - the user didn't select a 'commodity from' column *and* we didn't get a selected value either!
            // Note if you get here this suggests a bug in the code!
            error_message = _("No 'Commodity from' column selected and no selected Commodity specified either.\n"
                                       "This should never happen. Please report this as a bug.");
            PINFO("User warning: %s", error_message.c_str());
            throw std::invalid_argument(error_message);
        }
    }

    /* If column parsing was successful, convert price properties into a price. */
    try
    {
        price_properties_verify_essentials (parsed_line);

        QofBook* book = gnc_get_current_book();
        GNCPriceDB *pdb = gnc_pricedb_get_db (book);

        /* If all went well, add this price to the list. */
        auto price_created = price_props->create_price (book, pdb, m_over_write);
        if (price_created == ADDED)
            m_prices_added++;
        else if (price_created == DUPLICATED)
            m_prices_duplicated++;
        else if (price_created == REPLACED)
            m_prices_replaced++;
    }
    catch (const std::invalid_argument& e)
    {
        error_message = e.what();
        PINFO("User warning: %s", error_message.c_str());
    }
}