From 75209c024feb209b100b0bff3b6dacee12c7c8ca Mon Sep 17 00:00:00 2001 From: Dong Lin Date: Sun, 21 Feb 2021 13:03:39 -0800 Subject: [PATCH] Update price database for imported transactions When a transaction is added from the ledger, price database is updated properly. But if the transaction is imported, there is no price db update. This change adds the proper pricedb update in the import path (qfx/ofx/qif). Tested with make check --- bindings/engine.i | 1 + gnucash/import-export/import-backend.c | 5 +- gnucash/import-export/qif-imp/qif-to-gnc.scm | 3 +- gnucash/register/ledger-core/split-register.c | 82 +--------------- libgnucash/engine/Transaction.c | 94 +++++++++++++++++++ libgnucash/engine/Transaction.h | 10 ++ libgnucash/engine/gnc-pricedb.c | 1 + libgnucash/engine/gnc-pricedb.h | 1 + libgnucash/engine/mocks/gmock-Transaction.cpp | 7 ++ libgnucash/engine/mocks/gmock-Transaction.h | 1 + 10 files changed, 122 insertions(+), 83 deletions(-) diff --git a/bindings/engine.i b/bindings/engine.i index 89803bbc4f..e7af4047a9 100644 --- a/bindings/engine.i +++ b/bindings/engine.i @@ -421,6 +421,7 @@ void qof_book_set_string_option(QofBook* book, const char* opt_name, const char* SET_ENUM("PRICE-SOURCE-USER-PRICE"); SET_ENUM("PRICE-SOURCE-XFER-DLG-VAL"); SET_ENUM("PRICE-SOURCE-SPLIT-REG"); + SET_ENUM("PRICE-SOURCE-SPLIT-IMPORT"); SET_ENUM("PRICE-SOURCE-STOCK-SPLIT"); SET_ENUM("PRICE-SOURCE-TEMP"); SET_ENUM("PRICE-SOURCE-INVALID"); diff --git a/gnucash/import-export/import-backend.c b/gnucash/import-export/import-backend.c index df9410999b..e87e067baf 100644 --- a/gnucash/import-export/import-backend.c +++ b/gnucash/import-export/import-backend.c @@ -840,6 +840,7 @@ gnc_import_process_trans_item (GncImportMatchMap *matchmap, { Split * other_split; gnc_numeric imbalance_value; + Transaction *trans; /* DEBUG("Begin"); */ @@ -894,7 +895,9 @@ gnc_import_process_trans_item (GncImportMatchMap *matchmap, xaccSplitSetDateReconciledSecs(gnc_import_TransInfo_get_fsplit (trans_info), gnc_time (NULL)); /* Done editing. */ - xaccTransCommitEdit(gnc_import_TransInfo_get_trans (trans_info)); + trans = gnc_import_TransInfo_get_trans (trans_info); + xaccTransCommitEdit(trans); + xaccTransRecordPrice(trans, PRICE_SOURCE_SPLIT_IMPORT); return TRUE; case GNCImport_UPDATE: { diff --git a/gnucash/import-export/qif-imp/qif-to-gnc.scm b/gnucash/import-export/qif-imp/qif-to-gnc.scm index 0527fc3556..cc785e8253 100644 --- a/gnucash/import-export/qif-imp/qif-to-gnc.scm +++ b/gnucash/import-export/qif-imp/qif-to-gnc.scm @@ -444,7 +444,8 @@ progress-dialog) ;; rebalance and commit everything - (xaccTransCommitEdit gnc-xtn)))) + (xaccTransCommitEdit gnc-xtn) + (xaccTransRecordPrice gnc-xtn PRICE-SOURCE-SPLIT-IMPORT)))) (qif-file:xtns qif-file))) sorted-qif-files-list) diff --git a/gnucash/register/ledger-core/split-register.c b/gnucash/register/ledger-core/split-register.c index 6df61d6f0f..427aedc022 100644 --- a/gnucash/register/ledger-core/split-register.c +++ b/gnucash/register/ledger-core/split-register.c @@ -1958,6 +1958,7 @@ gnc_split_register_save (SplitRegister* reg, gboolean do_commit) } unreconcile_splits (reg); xaccTransCommitEdit (trans); + xaccTransRecordPrice (trans, PRICE_SOURCE_SPLIT_REG); } gnc_table_clear_current_cursor_changes (reg->table); @@ -2203,79 +2204,6 @@ recalculate_value (Split* split, SplitRegister* reg, } } -static void -record_price (SplitRegister* reg, Account* account, gnc_numeric value, - PriceSource source) -{ - Transaction* trans = gnc_split_register_get_current_trans (reg); - QofBook* book = qof_instance_get_book (QOF_INSTANCE (account)); - GNCPriceDB* pricedb = gnc_pricedb_get_db (book); - gnc_commodity* comm = xaccAccountGetCommodity (account); - gnc_commodity* curr = xaccTransGetCurrency (trans); - GNCPrice* price; - gnc_numeric price_value; - int scu = gnc_commodity_get_fraction (curr); - time64 time; - BasicCell* cell = gnc_table_layout_get_cell (reg->table->layout, DATE_CELL); - gboolean swap = FALSE; - - /* Only record the price for account types that don't have a - * "rate" cell. They'll get handled later by - * gnc_split_register_handle_exchange. - */ - if (gnc_split_reg_has_rate_cell (reg->type)) - return; - gnc_date_cell_get_date ((DateCell*)cell, &time, TRUE); - price = gnc_pricedb_lookup_day_t64 (pricedb, comm, curr, time); - if (gnc_commodity_equiv (comm, gnc_price_get_currency (price))) - swap = TRUE; - - if (price) - { - price_value = gnc_price_get_value (price); - if (gnc_numeric_equal (swap ? gnc_numeric_invert (value) : value, - price_value)) - { - gnc_price_unref (price); - return; - } - if (gnc_price_get_source (price) < PRICE_SOURCE_XFER_DLG_VAL) - { - /* Existing price is preferred over this one. */ - gnc_price_unref (price); - return; - } - if (swap) - { - value = gnc_numeric_invert (value); - scu = gnc_commodity_get_fraction (comm); - } - value = gnc_numeric_convert (value, scu * COMMODITY_DENOM_MULT, - GNC_HOW_RND_ROUND_HALF_UP); - gnc_price_begin_edit (price); - gnc_price_set_time64 (price, time); - gnc_price_set_source (price, source); - gnc_price_set_typestr (price, PRICE_TYPE_TRN); - gnc_price_set_value (price, value); - gnc_price_commit_edit (price); - gnc_price_unref (price); - return; - } - - value = gnc_numeric_convert (value, scu * COMMODITY_DENOM_MULT, - GNC_HOW_RND_ROUND_HALF_UP); - price = gnc_price_create (book); - gnc_price_begin_edit (price); - gnc_price_set_commodity (price, comm); - gnc_price_set_currency (price, curr); - gnc_price_set_time64 (price, time); - gnc_price_set_source (price, source); - gnc_price_set_typestr (price, PRICE_TYPE_TRN); - gnc_price_set_value (price, value); - gnc_pricedb_add_price (pricedb, price); - gnc_price_commit_edit (price); -} - static gboolean gnc_split_register_auto_calc (SplitRegister* reg, Split* split) { @@ -2447,14 +2375,6 @@ gnc_split_register_auto_calc (SplitRegister* reg, Split* split) if (recalc_value) recalculate_value (split, reg, price, amount, shares_changed); - if (price_changed) - { - cell = (PriceCell*) gnc_table_layout_get_cell (reg->table->layout, - PRIC_CELL); - price = gnc_price_cell_get_value (cell); - if (gnc_numeric_positive_p (price)) - record_price (reg, account, price, source); - } return TRUE; } diff --git a/libgnucash/engine/Transaction.c b/libgnucash/engine/Transaction.c index 7a586bd3c2..0031a7bb30 100644 --- a/libgnucash/engine/Transaction.c +++ b/libgnucash/engine/Transaction.c @@ -2938,6 +2938,100 @@ xaccTransFindSplitByAccount(const Transaction *trans, const Account *acc) return NULL; } +static void +record_price (Split *split, + PriceSource source) +{ + Transaction *trans; + Account *account; + QofBook* book; + GNCPriceDB* pricedb; + gnc_commodity* comm; + gnc_commodity* curr; + GNCPrice* price; + gnc_numeric price_value, value, amount; + int scu; + time64 time; + gboolean swap; + + account = xaccSplitGetAccount (split); + if (!xaccAccountIsPriced (account)) + { + return; + } + amount = xaccSplitGetAmount (split); + if (gnc_numeric_zero_p (amount)) + { + return; + } + trans = xaccSplitGetParent (split); + value = gnc_numeric_div (xaccSplitGetValue (split), amount, + GNC_DENOM_AUTO, + GNC_HOW_DENOM_EXACT); + book = qof_instance_get_book (QOF_INSTANCE (account)); + pricedb = gnc_pricedb_get_db (book); + comm = xaccAccountGetCommodity (account); + curr = xaccTransGetCurrency (trans); + scu = gnc_commodity_get_fraction (curr); + swap = FALSE; + time = xaccTransGetDate (trans); + price = gnc_pricedb_lookup_day_t64 (pricedb, comm, curr, time); + if (gnc_commodity_equiv (comm, gnc_price_get_currency (price))) + swap = TRUE; + + if (price) + { + price_value = gnc_price_get_value (price); + if (gnc_numeric_equal (swap ? gnc_numeric_invert (value) : value, + price_value)) + { + gnc_price_unref (price); + return; + } + if (gnc_price_get_source (price) < source) + { + /* Existing price is preferred over this one. */ + gnc_price_unref (price); + return; + } + if (swap) + { + value = gnc_numeric_invert (value); + scu = gnc_commodity_get_fraction (comm); + } + value = gnc_numeric_convert (value, scu * COMMODITY_DENOM_MULT, + GNC_HOW_RND_ROUND_HALF_UP); + gnc_price_begin_edit (price); + gnc_price_set_time64 (price, time); + gnc_price_set_source (price, source); + gnc_price_set_typestr (price, PRICE_TYPE_TRN); + gnc_price_set_value (price, value); + gnc_price_commit_edit (price); + gnc_price_unref (price); + return; + } + + value = gnc_numeric_convert (value, scu * COMMODITY_DENOM_MULT, + GNC_HOW_RND_ROUND_HALF_UP); + price = gnc_price_create (book); + gnc_price_begin_edit (price); + gnc_price_set_commodity (price, comm); + gnc_price_set_currency (price, curr); + gnc_price_set_time64 (price, time); + gnc_price_set_source (price, source); + gnc_price_set_typestr (price, PRICE_TYPE_TRN); + gnc_price_set_value (price, value); + gnc_pricedb_add_price (pricedb, price); + gnc_price_commit_edit (price); +} + +void +xaccTransRecordPrice (Transaction *trans, PriceSource source) +{ + /* XXX: This should have been part of xaccSplitCommitEdit. */ + for (GList *n = xaccTransGetSplitList (trans); n; n = n->next) + record_price (n->data, source); +} /********************************************************************\ \********************************************************************/ diff --git a/libgnucash/engine/Transaction.h b/libgnucash/engine/Transaction.h index 5815c8f447..0389960b2f 100644 --- a/libgnucash/engine/Transaction.h +++ b/libgnucash/engine/Transaction.h @@ -92,6 +92,7 @@ typedef struct _TransactionClass TransactionClass; #include "gnc-commodity.h" #include "gnc-engine.h" +#include "gnc-pricedb.h" #include "Split.h" #ifdef __cplusplus @@ -767,6 +768,15 @@ time64 xaccTransGetVoidTime(const Transaction *tr); void xaccTransDump (const Transaction *trans, const char *tag); #endif +/** The xaccTransRecordPrice() method iterates through the splits and + * and record the non-currency equivalent prices in the price database. + * + * @param trans The transaction whose price is recorded + * @param source The price priority level + */ +void xaccTransRecordPrice (Transaction *trans, PriceSource source); + + #define RECONCILED_MATCH_TYPE "reconciled-match" /** \deprecated */ diff --git a/libgnucash/engine/gnc-pricedb.c b/libgnucash/engine/gnc-pricedb.c index a59c5e1091..4d3bc23c2b 100644 --- a/libgnucash/engine/gnc-pricedb.c +++ b/libgnucash/engine/gnc-pricedb.c @@ -124,6 +124,7 @@ static const char* source_names[(size_t)PRICE_SOURCE_INVALID + 1] = /* String retained for backwards compatibility. */ "user:xfer-dialog", "user:split-register", + "user:split-import", "user:stock-split", "user:invoice-post", /* Retained for backwards compatibility */ "temporary", diff --git a/libgnucash/engine/gnc-pricedb.h b/libgnucash/engine/gnc-pricedb.h index 5010476c3e..1d9db26e4f 100644 --- a/libgnucash/engine/gnc-pricedb.h +++ b/libgnucash/engine/gnc-pricedb.h @@ -173,6 +173,7 @@ typedef enum PRICE_SOURCE_USER_PRICE, // "user:price" PRICE_SOURCE_XFER_DLG_VAL, // "user:xfer-dialog" PRICE_SOURCE_SPLIT_REG, // "user:split-register" + PRICE_SOURCE_SPLIT_IMPORT, // "user:split-import" PRICE_SOURCE_STOCK_SPLIT, // "user:stock-split" PRICE_SOURCE_INVOICE, // "user:invoice-post" PRICE_SOURCE_TEMP, // "temporary" diff --git a/libgnucash/engine/mocks/gmock-Transaction.cpp b/libgnucash/engine/mocks/gmock-Transaction.cpp index 40cfdec3a1..331c849592 100644 --- a/libgnucash/engine/mocks/gmock-Transaction.cpp +++ b/libgnucash/engine/mocks/gmock-Transaction.cpp @@ -144,3 +144,10 @@ xaccTransDestroy (Transaction *trans) ASSERT_TRUE(GNC_IS_MOCKTRANSACTION(trans)); gnc_mocktransaction(trans)->destroy(); } + +void +xaccTransRecordPrice (Transaction *trans, PriceSource source) +{ + g_return_if_fail(GNC_IS_MOCKTRANSACTION(trans)); + ((MockTransaction*)trans)->recordPrice(); +} diff --git a/libgnucash/engine/mocks/gmock-Transaction.h b/libgnucash/engine/mocks/gmock-Transaction.h index 885eebf68d..61fc4a4ae6 100644 --- a/libgnucash/engine/mocks/gmock-Transaction.h +++ b/libgnucash/engine/mocks/gmock-Transaction.h @@ -66,6 +66,7 @@ public: MOCK_CONST_METHOD0(get_num, const char *()); MOCK_CONST_METHOD0(is_open, gboolean()); MOCK_METHOD0(destroy, void()); + MOCK_METHOD0(recordPrice, void()); protected: // Protect destructor to avoid MockTransaction objects to be created on stack. MockTransaction