From 1d4e522597697c5ec211d7e9a08bd14a19fedb77 Mon Sep 17 00:00:00 2001 From: Christopher Lam Date: Thu, 25 Aug 2022 20:25:00 +0800 Subject: [PATCH 1/3] [assistant-stock-transaction] refactor create_split takes gnc_numeric instead of GtkWidget* --- gnucash/gnome/assistant-stock-transaction.cpp | 45 +++++++++++-------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/gnucash/gnome/assistant-stock-transaction.cpp b/gnucash/gnome/assistant-stock-transaction.cpp index e266f99a5c..9c9f0da510 100644 --- a/gnucash/gnome/assistant-stock-transaction.cpp +++ b/gnucash/gnome/assistant-stock-transaction.cpp @@ -922,12 +922,9 @@ static void create_split (Transaction *trans, FieldMask splitfield, const gchar *action, Account *account, AccountVec& account_commits, GtkWidget *memo_entry, - GtkWidget *amount, GtkWidget *value, - bool skip_if_zero) + gnc_numeric amount_numeric, gnc_numeric value_numeric, + bool skip_if_zero, StockTransactionInfo *info) { - auto amount_numeric = amount ? gnc_amount_edit_get_amount (GNC_AMOUNT_EDIT (amount)) : gnc_numeric_zero (); - auto value_numeric = value ? gnc_amount_edit_get_amount (GNC_AMOUNT_EDIT (value)) : gnc_numeric_zero (); - if (skip_if_zero && gnc_numeric_zero_p (value_numeric)) return; @@ -982,6 +979,11 @@ add_price (GtkWidget *amount, GtkWidget *value, gnc_price_unref (price); } +static gnc_numeric gae_amount (GtkWidget *widget) +{ + return gnc_amount_edit_get_amount (GNC_AMOUNT_EDIT (widget)); +} + void stock_assistant_finish (GtkAssistant *assistant, gpointer user_data) { @@ -1001,52 +1003,59 @@ stock_assistant_finish (GtkAssistant *assistant, gpointer user_data) auto date = gnc_date_edit_get_date (GNC_DATE_EDIT (info->date_edit)); xaccTransSetDatePostedSecsNormalized (trans, date); + auto stock_amount = info->txn_type->stock_amount != FieldMask::DISABLED ? + gae_amount (info->stock_amount_edit) : gnc_numeric_zero (); + auto stock_value = info->txn_type->stock_value != FieldMask::DISABLED ? + gae_amount (info->stock_value_edit) : gnc_numeric_zero (); create_split (trans, info->txn_type->stock_amount | info->txn_type->stock_value, NC_ ("Stock Assistant: Action field", "Stock"), info->acct, account_commits, info->stock_memo_edit, - info->txn_type->stock_amount != FieldMask::DISABLED ? info->stock_amount_edit : nullptr, - info->txn_type->stock_value != FieldMask::DISABLED ? info->stock_value_edit : nullptr, - false); + stock_amount, stock_value, false, info); if (info->txn_type->cash_value != FieldMask::DISABLED) + { + auto cash = gae_amount (info->cash_value); create_split (trans, info->txn_type->cash_value, NC_ ("Stock Assistant: Action field", "Cash"), - gas_account (info->cash_account), - account_commits, info->cash_memo_edit, info->cash_value, - info->cash_value, false); + gas_account (info->cash_account), account_commits, + info->cash_memo_edit, cash, cash, false, info); + } if (info->txn_type->fees_value != FieldMask::DISABLED) { + auto fees = gae_amount (info->fees_value); auto capitalize = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (info->capitalize_fees_checkbox)); create_split (trans, info->txn_type->fees_value, NC_ ("Stock Assistant: Action field", "Fees"), capitalize ? info->acct : gas_account (info->fees_account), account_commits, info->fees_memo_edit, - capitalize ? nullptr : info->fees_value, - info->fees_value, true); + capitalize ? gnc_numeric_zero () : fees, fees, true, info); } if (info->txn_type->dividend_value != FieldMask::DISABLED) + { + auto dividend = gae_amount (info->dividend_value); create_split (trans, info->txn_type->dividend_value, NC_ ("Stock Assistant: Action field", "Dividend"), - gas_account (info->dividend_account), - account_commits, info->dividend_memo_edit, - info->dividend_value, info->dividend_value, false); + gas_account (info->dividend_account), account_commits, + info->dividend_memo_edit, dividend, dividend, false, info); + } if (info->txn_type->capgains_value != FieldMask::DISABLED) { + auto capgains = gae_amount (info->capgains_value); create_split (trans, info->txn_type->capgains_value, NC_ ("Stock Assistant: Action field", "Capital Gain"), gas_account (info->capgains_account), account_commits, info->capgains_memo_edit, - info->capgains_value, info->capgains_value, false); + capgains, capgains, false, info); create_split (trans, info->txn_type->capgains_value ^ (FieldMask::ENABLED_CREDIT | FieldMask::ENABLED_DEBIT), NC_ ("Stock Assistant: Action field", "Capital Gain"), info->acct, account_commits, info->capgains_memo_edit, - nullptr, info->capgains_value, false); + gnc_numeric_zero (), capgains, false, info); } if (info->txn_type->stock_amount != FieldMask::DISABLED && From ab06e1e0659d0d3236e1aad86cccc6084ed448d6 Mon Sep 17 00:00:00 2001 From: Christopher Lam Date: Tue, 23 Aug 2022 22:32:20 +0800 Subject: [PATCH 2/3] [assistant-stock-transaction] specialises stock_amount input --- gnucash/gnome/assistant-stock-transaction.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/gnucash/gnome/assistant-stock-transaction.cpp b/gnucash/gnome/assistant-stock-transaction.cpp index 9c9f0da510..7312cc3aa6 100644 --- a/gnucash/gnome/assistant-stock-transaction.cpp +++ b/gnucash/gnome/assistant-stock-transaction.cpp @@ -113,6 +113,7 @@ FieldMask operator ^(FieldMask lhs, FieldMask rhs) struct TxnTypeInfo { FieldMask stock_amount; + bool input_new_balance; FieldMask stock_value; FieldMask cash_value; FieldMask fees_value; @@ -131,6 +132,7 @@ static const TxnTypeVec starting_types { { FieldMask::ENABLED_DEBIT, // stock_amt + false, // input_new_balance FieldMask::ENABLED_DEBIT, // stock_val FieldMask::ENABLED_CREDIT, // cash_amt FieldMask::ENABLED_DEBIT | FieldMask::ALLOW_ZERO, // fees_amt @@ -144,6 +146,7 @@ static const TxnTypeVec starting_types }, { FieldMask::ENABLED_CREDIT, // stock_amt + false, // input_new_balance FieldMask::ENABLED_CREDIT, // stock_val FieldMask::ENABLED_DEBIT, // cash_amt FieldMask::ENABLED_DEBIT | FieldMask::ALLOW_ZERO, // fees_amt @@ -161,6 +164,7 @@ static const TxnTypeVec long_types { { FieldMask::ENABLED_DEBIT, // stock_amt + false, // input_new_balance FieldMask::ENABLED_DEBIT, // stock_val FieldMask::ENABLED_CREDIT, // cash_amt FieldMask::ENABLED_DEBIT | FieldMask::ALLOW_ZERO, // fees_amt @@ -174,6 +178,7 @@ static const TxnTypeVec long_types }, { FieldMask::ENABLED_CREDIT, // stock_amt + false, // input_new_balance FieldMask::ENABLED_CREDIT, // stock_val FieldMask::ENABLED_DEBIT, // cash_amt FieldMask::ENABLED_DEBIT | FieldMask::ALLOW_ZERO, // fees_amt @@ -187,6 +192,7 @@ static const TxnTypeVec long_types }, { FieldMask::DISABLED, // stock_amt + false, // input_new_balance FieldMask::DISABLED, // stock_val FieldMask::ENABLED_DEBIT, // cash_amt FieldMask::ENABLED_DEBIT | FieldMask::ALLOW_ZERO, // fees_amt @@ -201,6 +207,7 @@ reinvested must be subsequently recorded as a regular stock purchase.") }, { FieldMask::DISABLED, // stock_amt + false, // input_new_balance FieldMask::ENABLED_CREDIT, // stock_val FieldMask::ENABLED_DEBIT, // cash_amt FieldMask::ENABLED_DEBIT | FieldMask::ALLOW_ZERO, // fees_amt @@ -214,6 +221,7 @@ reinvested must be subsequently recorded as a regular stock purchase.") }, { FieldMask::DISABLED, // stock_amt + false, // input_new_balance FieldMask::ENABLED_DEBIT, // stock_val FieldMask::DISABLED, // cash_amt FieldMask::ENABLED_DEBIT | FieldMask::ALLOW_ZERO, // fees_amt @@ -227,6 +235,7 @@ reinvested must be subsequently recorded as a regular stock purchase.") }, { FieldMask::ENABLED_DEBIT, // stock_amt + true, // input_new_balance FieldMask::DISABLED, // stock_val FieldMask::DISABLED, // cash_amt FieldMask::ENABLED_DEBIT | FieldMask::ALLOW_ZERO, // fees_amt @@ -240,6 +249,7 @@ reinvested must be subsequently recorded as a regular stock purchase.") }, { FieldMask::ENABLED_CREDIT, // stock_amt + true, // input_new_balance FieldMask::DISABLED, // stock_val FieldMask::DISABLED, // cash_amt FieldMask::ENABLED_DEBIT | FieldMask::ALLOW_ZERO, // fees_amt @@ -260,6 +270,7 @@ static const TxnTypeVec short_types { { FieldMask::ENABLED_CREDIT, // stock_amt + false, // input_new_balance FieldMask::ENABLED_CREDIT, // stock_val FieldMask::ENABLED_DEBIT, // cash_amt FieldMask::ENABLED_DEBIT | FieldMask::ALLOW_ZERO, // fees_amt @@ -273,6 +284,7 @@ static const TxnTypeVec short_types }, { FieldMask::ENABLED_DEBIT, // stock_amt + false, // input_new_balance FieldMask::ENABLED_DEBIT, // stock_val FieldMask::ENABLED_CREDIT, // cash_amt FieldMask::ENABLED_DEBIT | FieldMask::ALLOW_ZERO, // fees_amt @@ -286,6 +298,7 @@ static const TxnTypeVec short_types }, { FieldMask::DISABLED, // stock_amt + false, // input_new_balance FieldMask::DISABLED, // stock_val FieldMask::ENABLED_CREDIT, // cash_amt FieldMask::ENABLED_DEBIT | FieldMask::ALLOW_ZERO, // fees_amt @@ -299,6 +312,7 @@ static const TxnTypeVec short_types }, { FieldMask::DISABLED, // stock_amt + false, // input_new_balance FieldMask::ENABLED_DEBIT, // stock_val FieldMask::ENABLED_CREDIT, // cash_amt FieldMask::ENABLED_DEBIT | FieldMask::ALLOW_ZERO, // fees_amt @@ -312,6 +326,7 @@ static const TxnTypeVec short_types }, { FieldMask::DISABLED, // stock_amt + false, // input_new_balance FieldMask::ENABLED_CREDIT, // stock_val FieldMask::DISABLED, // cash_amt FieldMask::ENABLED_DEBIT | FieldMask::ALLOW_ZERO, // fees_amt @@ -325,6 +340,7 @@ static const TxnTypeVec short_types }, { FieldMask::ENABLED_CREDIT, // stock_amt + true, // input_new_balance FieldMask::DISABLED, // stock_val FieldMask::DISABLED, // cash_amt FieldMask::ENABLED_DEBIT | FieldMask::ALLOW_ZERO, // fees_amt @@ -338,6 +354,7 @@ static const TxnTypeVec short_types }, { FieldMask::ENABLED_DEBIT, // stock_amt + true, // input_new_balance FieldMask::DISABLED, // stock_val FieldMask::DISABLED, // cash_amt FieldMask::ENABLED_DEBIT | FieldMask::ALLOW_ZERO, // fees_amt From 723189b26c6c8e7d5a638b5f6e88ed054fd4250c Mon Sep 17 00:00:00 2001 From: Christopher Lam Date: Tue, 23 Aug 2022 22:34:14 +0800 Subject: [PATCH 3/3] [assistant-stock-transaction] special stock_amount input stock splits --- gnucash/gnome/assistant-stock-transaction.cpp | 66 ++++++++++++++++++- 1 file changed, 64 insertions(+), 2 deletions(-) diff --git a/gnucash/gnome/assistant-stock-transaction.cpp b/gnucash/gnome/assistant-stock-transaction.cpp index 7312cc3aa6..03b9105b89 100644 --- a/gnucash/gnome/assistant-stock-transaction.cpp +++ b/gnucash/gnome/assistant-stock-transaction.cpp @@ -399,9 +399,12 @@ typedef struct // stock amount page gnc_numeric balance_at_date; GtkWidget * stock_amount_page; + GtkWidget * stock_amount_title; GtkWidget * prev_amount; GtkWidget * next_amount; + GtkWidget * next_amount_label; GtkWidget * stock_amount_edit; + GtkWidget * stock_amount_label; // stock value page GtkWidget * stock_value_page; @@ -501,6 +504,24 @@ refresh_page_stock_amount (GtkWidget *widget, gpointer user_data) if (gnc_amount_edit_expr_is_valid (GNC_AMOUNT_EDIT (info->stock_amount_edit), &stock_amount, true, nullptr)) gtk_label_set_text (GTK_LABEL(info->next_amount), nullptr); + else if (info->txn_type->input_new_balance) + { + gnc_numeric ratio = gnc_numeric_div (stock_amount, bal, + GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE); + if (gnc_numeric_check (ratio) || gnc_numeric_negative_p (ratio)) + gtk_label_set_text (GTK_LABEL(info->next_amount), nullptr); + else + { + auto str = gnc_numeric_to_string (ratio); + auto p = str ? strchr (str, '/') : nullptr; + if (p) + *p = ':'; + auto lbl = g_strdup_printf (_("%s Split"), str); + gtk_label_set_text (GTK_LABEL(info->next_amount), lbl); + g_free (lbl); + g_free (str); + } + } else { if (info->txn_type->stock_amount == FieldMask::ENABLED_CREDIT) @@ -721,7 +742,30 @@ to ensure proper recording."), new_date_str, last_split_date_str); NC_ ("Stock Assistant: Page name", "stock value"), errors); - if (info->txn_type->stock_amount != FieldMask::DISABLED) + if (info->txn_type->stock_amount == FieldMask::DISABLED) + ; + else if (info->txn_type->input_new_balance) + { + auto stock_amount = gnc_amount_edit_get_amount + (GNC_AMOUNT_EDIT(info->stock_amount_edit)); + auto credit_side = (info->txn_type->stock_amount & FieldMask::ENABLED_CREDIT); + auto delta = gnc_numeric_sub_fixed (stock_amount, info->balance_at_date); + auto ratio = gnc_numeric_div (stock_amount, info->balance_at_date, + GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE); + auto stock_pinfo = gnc_commodity_print_info + (xaccAccountGetCommodity (info->acct), true); + stock_amount = gnc_numeric_sub_fixed (stock_amount, info->balance_at_date); + line.units = xaccPrintAmount (stock_amount, stock_pinfo); + if (gnc_numeric_check (ratio)) + add_error_str (errors, N_("Invalid stock new balance")); + else if (gnc_numeric_negative_p (ratio)) + add_error_str (errors, N_("New and old balance must have same signs")); + else if (gnc_numeric_negative_p (delta) && !credit_side) + add_error_str (errors, N_("New balance must be higher than old balance")); + else if (gnc_numeric_positive_p (delta) && credit_side) + add_error_str (errors, N_("New balance must be lower than old balance")); + } + else { auto stock_amount = gnc_amount_edit_get_amount (GNC_AMOUNT_EDIT(info->stock_amount_edit)); @@ -880,6 +924,17 @@ stock_assistant_prepare (GtkAssistant *assistant, GtkWidget *page, case PAGE_STOCK_AMOUNT: info->balance_at_date = xaccAccountGetBalanceAsOfDate (info->acct, gnc_date_edit_get_date_end (GNC_DATE_EDIT (info->date_edit))); + gtk_label_set_text_with_mnemonic + (GTK_LABEL (info->stock_amount_label), + info->txn_type->input_new_balance ? _("Ne_w Balance") : _("_Shares")); + gtk_label_set_text + (GTK_LABEL (info->next_amount_label), + info->txn_type->input_new_balance ? _("Ratio") : _("Next Balance")); + gtk_label_set_text + (GTK_LABEL (info->stock_amount_title), + info->txn_type->input_new_balance ? + _("Enter the new balance of shares after the stock split.") : + _("Enter the number of shares you gained or lost in the transaction.")); refresh_page_stock_amount (info->stock_amount_edit, info); // fixme: the following doesn't work??? gtk_widget_grab_focus (gnc_amount_edit_gtk_entry @@ -945,7 +1000,9 @@ create_split (Transaction *trans, FieldMask splitfield, if (skip_if_zero && gnc_numeric_zero_p (value_numeric)) return; - if (splitfield & FieldMask::ENABLED_CREDIT) + if (info->txn_type->input_new_balance) + amount_numeric = gnc_numeric_sub_fixed (amount_numeric, info->balance_at_date); + else if (splitfield & FieldMask::ENABLED_CREDIT) { amount_numeric = gnc_numeric_neg (amount_numeric); value_numeric = gnc_numeric_neg (value_numeric); @@ -1024,6 +1081,8 @@ stock_assistant_finish (GtkAssistant *assistant, gpointer user_data) gae_amount (info->stock_amount_edit) : gnc_numeric_zero (); auto stock_value = info->txn_type->stock_value != FieldMask::DISABLED ? gae_amount (info->stock_value_edit) : gnc_numeric_zero (); + if (info->txn_type->input_new_balance) + stock_amount = gnc_numeric_sub_fixed (stock_amount, info->balance_at_date); create_split (trans, info->txn_type->stock_amount | info->txn_type->stock_value, NC_ ("Stock Assistant: Action field", "Stock"), info->acct, account_commits, info->stock_memo_edit, @@ -1232,9 +1291,12 @@ stock_assistant_create (StockTransactionInfo *info) /* Stock Amount Page Widgets */ info->stock_amount_page = get_widget (builder, "stock_amount_page"); + info->stock_amount_title = get_widget (builder, "stock_amount_title"); info->prev_amount = get_widget (builder, "prev_balance_amount"); + info->stock_amount_label = get_widget (builder, "stock_amount_label"); info->stock_amount_edit = create_gae (builder, 1, xaccAccountGetCommodity (info->acct), "stock_amount_table", "stock_amount_label"); info->next_amount = get_widget (builder, "next_balance_amount"); + info->next_amount_label = get_widget (builder, "next_balance_label"); g_signal_connect (info->stock_amount_edit, "changed", G_CALLBACK (refresh_page_stock_amount), info);