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 ==================== */
|