Branch data Line data Source code
1 : : /********************************************************************\
2 : : * ScrubBudget.c -- fix budget amount signs *
3 : : * Copyright (c) 2020 Christoher Lam *
4 : : * *
5 : : * This program is free software; you can redistribute it and/or *
6 : : * modify it under the terms of the GNU General Public License as *
7 : : * published by the Free Software Foundation; either version 2 of *
8 : : * the License, or (at your option) any later version. *
9 : : * *
10 : : * This program is distributed in the hope that it will be useful, *
11 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 : : * GNU General Public License for more details. *
14 : : * *
15 : : * You should have received a copy of the GNU General Public License*
16 : : * along with this program; if not, contact: *
17 : : * *
18 : : * Free Software Foundation Voice: +1-617-542-5942 *
19 : : * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
20 : : * Boston, MA 02110-1301, USA gnu@gnu.org *
21 : : * *
22 : : \********************************************************************/
23 : :
24 : : #include <config.h>
25 : :
26 : : #include <glib.h>
27 : : #include <glib/gi18n.h>
28 : : #include <stdio.h>
29 : : #include <string.h>
30 : : #include <stdint.h>
31 : :
32 : : #include "gnc-prefs.h"
33 : : #include "gnc-budget.h"
34 : : #include "gnc-features.h"
35 : : #include "ScrubBudget.h"
36 : :
37 : : static QofLogModule log_module = "gnc.engine.scrub";
38 : :
39 : : typedef enum
40 : : {
41 : : HEURISTICS_INC_EXP,
42 : : HEURISTICS_CREDIT_ACC,
43 : : HEURISTICS_NONE
44 : : } SignReversals;
45 : :
46 : : typedef struct
47 : : {
48 : : GncBudget* budget;
49 : : SignReversals policy;
50 : : } ReversalType;
51 : :
52 : : typedef struct
53 : : {
54 : : gint asset,liability,equity,income,expense;
55 : : gint num_periods;
56 : : GncBudget* budget;
57 : : } ProcessData;
58 : :
59 : : static void
60 : 0 : process_heuristics_acct (Account * account, gpointer user_data)
61 : : {
62 : : /* each account- check type. sum budget period amounts. if sum<0,
63 : : decrease type tally by 1. if sum>0, increase type tally by 1. */
64 : 0 : ProcessData *heuristics = (ProcessData*) user_data;
65 : 0 : gnc_numeric total = gnc_numeric_zero(), val;
66 : : gint sign;
67 : : gchar *totalstr;
68 : :
69 : 0 : for (gint i = 0; i < heuristics->num_periods; ++i)
70 : : {
71 : 0 : if (!gnc_budget_is_account_period_value_set (heuristics->budget, account, i))
72 : 0 : continue;
73 : 0 : val = gnc_budget_get_account_period_value (heuristics->budget, account, i);
74 : 0 : total = gnc_numeric_add_fixed (total, val);
75 : : }
76 : :
77 : 0 : sign = gnc_numeric_compare (total, gnc_numeric_zero ());
78 : 0 : totalstr = gnc_numeric_to_string (total);
79 : 0 : PINFO ("acct=%s, total=%s, sign=%d",
80 : : xaccAccountGetName (account), totalstr, sign);
81 : 0 : g_free (totalstr);
82 : :
83 : 0 : switch (xaccAccountTypeGetFundamental (xaccAccountGetType (account)))
84 : : {
85 : 0 : case ACCT_TYPE_ASSET:
86 : 0 : heuristics->asset += sign;
87 : 0 : break;
88 : 0 : case ACCT_TYPE_LIABILITY:
89 : 0 : heuristics->liability += sign;
90 : 0 : break;
91 : 0 : case ACCT_TYPE_EXPENSE:
92 : 0 : heuristics->expense += sign;
93 : 0 : break;
94 : 0 : case ACCT_TYPE_INCOME:
95 : 0 : heuristics->income += sign;
96 : 0 : break;
97 : 0 : case ACCT_TYPE_EQUITY:
98 : 0 : heuristics->equity += sign;
99 : 0 : break;
100 : 0 : default:
101 : 0 : break;
102 : : }
103 : 0 : }
104 : :
105 : : static SignReversals
106 : 0 : heuristics_on_budget (GncBudget * budget, Account *root)
107 : : {
108 : 0 : ProcessData heuristics = {0, 0, 0, 0, 0, gnc_budget_get_num_periods (budget),
109 : : budget};
110 : : SignReversals result;
111 : :
112 : 0 : gnc_account_foreach_descendant (root, process_heuristics_acct, &heuristics);
113 : :
114 : 0 : result =
115 : 0 : heuristics.expense < 0 ? HEURISTICS_INC_EXP :
116 : 0 : heuristics.income < 0 ? HEURISTICS_NONE :
117 : : HEURISTICS_CREDIT_ACC;
118 : :
119 : 0 : LEAVE ("heuristics_on_budget %s: A(%d) L(%d) Inc(%d) Exp(%d) Eq(%d) = %d",
120 : : gnc_budget_get_name (budget),
121 : : heuristics.asset, heuristics.liability, heuristics.income,
122 : : heuristics.expense, heuristics.equity, result);
123 : :
124 : 0 : return result;
125 : : }
126 : :
127 : : static void
128 : 0 : fix_budget_acc_sign (Account *acc, gpointer user_data)
129 : : {
130 : 0 : ReversalType* reversal = (ReversalType*) user_data;
131 : 0 : GncBudget* budget = reversal->budget;
132 : 0 : guint numperiods = gnc_budget_get_num_periods (budget);
133 : 0 : int type = xaccAccountTypeGetFundamental (xaccAccountGetType (acc));
134 : :
135 : 0 : ENTER ("budget account reversal [%s] starting", xaccAccountGetName(acc));
136 : :
137 : 0 : switch (reversal->policy)
138 : : {
139 : 0 : case HEURISTICS_INC_EXP:
140 : 0 : if ((type != ACCT_TYPE_INCOME) && (type != ACCT_TYPE_EXPENSE))
141 : 0 : return;
142 : 0 : PINFO ("budget account [%s] is inc/exp. reverse!",
143 : : xaccAccountGetName(acc));
144 : 0 : break;
145 : 0 : case HEURISTICS_CREDIT_ACC:
146 : 0 : if ((type != ACCT_TYPE_LIABILITY) &&
147 : 0 : (type != ACCT_TYPE_EQUITY) &&
148 : : (type != ACCT_TYPE_INCOME))
149 : 0 : return;
150 : 0 : PINFO ("budget account [%s] is credit-account. reverse!",
151 : : xaccAccountGetName(acc));
152 : 0 : break;
153 : 0 : default:
154 : : /* shouldn't happen. */
155 : 0 : return;
156 : : }
157 : :
158 : 0 : for (guint i=0; i < numperiods; ++i)
159 : : {
160 : : gnc_numeric amt;
161 : 0 : if (!gnc_budget_is_account_period_value_set (budget, acc, i))
162 : 0 : continue;
163 : :
164 : 0 : amt = gnc_budget_get_account_period_value (budget, acc, i);
165 : 0 : amt = gnc_numeric_neg (amt);
166 : 0 : gnc_budget_set_account_period_value (budget, acc, i, amt);
167 : : }
168 : :
169 : 0 : LEAVE ("budget account reversal [%s] completed!", xaccAccountGetName(acc));
170 : : }
171 : :
172 : : static void
173 : 0 : maybe_scrub_budget (QofInstance* data, gpointer user_data)
174 : : {
175 : 0 : GncBudget* budget = GNC_BUDGET(data);
176 : 0 : Account *root = (Account*) user_data;
177 : : ReversalType reversal;
178 : :
179 : 0 : reversal.policy = heuristics_on_budget (budget, root);
180 : 0 : if (reversal.policy == HEURISTICS_NONE)
181 : : {
182 : 0 : PWARN ("budget [%s] doesn't need reversing", gnc_budget_get_name (budget));
183 : 0 : return;
184 : : }
185 : :
186 : 0 : reversal.budget = budget;
187 : :
188 : 0 : ENTER ("processing budget [%s] for reversal", gnc_budget_get_name (budget));
189 : 0 : gnc_account_foreach_descendant (root, fix_budget_acc_sign, &reversal);
190 : 0 : LEAVE ("completed budget [%s] for reversal", gnc_budget_get_name (budget));
191 : : }
192 : :
193 : : gboolean
194 : 0 : gnc_maybe_scrub_all_budget_signs (QofBook *book)
195 : : {
196 : 0 : QofCollection* collection = qof_book_get_collection (book, GNC_ID_BUDGET);
197 : 0 : gboolean has_no_budgets = (qof_collection_count (collection) == 0);
198 : 0 : gboolean featured = gnc_features_check_used (book, GNC_FEATURE_BUDGET_UNREVERSED);
199 : :
200 : : /* If there are no budgets, there shouldn't be feature! */
201 : 0 : if (has_no_budgets && featured)
202 : : {
203 : 0 : gnc_features_set_unused (book, GNC_FEATURE_BUDGET_UNREVERSED);
204 : 0 : PWARN ("There are no budgets, removing feature BUDGET_UNREVERSED");
205 : : }
206 : :
207 : 0 : if (has_no_budgets || featured)
208 : 0 : return FALSE;
209 : :
210 : : /* There are budgets and feature is not set. Scrub, and set
211 : : feature. Return TRUE to show budget fix warning. */
212 : 0 : qof_collection_foreach (collection, maybe_scrub_budget,
213 : 0 : gnc_book_get_root_account (book));
214 : 0 : gnc_features_set_used (book, GNC_FEATURE_BUDGET_UNREVERSED);
215 : 0 : return TRUE;
216 : : }
217 : : /* ==================== END OF FILE ==================== */
|