GnuCash  5.6-150-g038405b370+
ScrubBudget.c
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 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  ProcessData *heuristics = (ProcessData*) user_data;
65  gnc_numeric total = gnc_numeric_zero(), val;
66  gint sign;
67  gchar *totalstr;
68 
69  for (gint i = 0; i < heuristics->num_periods; ++i)
70  {
71  if (!gnc_budget_is_account_period_value_set (heuristics->budget, account, i))
72  continue;
73  val = gnc_budget_get_account_period_value (heuristics->budget, account, i);
74  total = gnc_numeric_add_fixed (total, val);
75  }
76 
77  sign = gnc_numeric_compare (total, gnc_numeric_zero ());
78  totalstr = gnc_numeric_to_string (total);
79  PINFO ("acct=%s, total=%s, sign=%d",
80  xaccAccountGetName (account), totalstr, sign);
81  g_free (totalstr);
82 
84  {
85  case ACCT_TYPE_ASSET:
86  heuristics->asset += sign;
87  break;
89  heuristics->liability += sign;
90  break;
91  case ACCT_TYPE_EXPENSE:
92  heuristics->expense += sign;
93  break;
94  case ACCT_TYPE_INCOME:
95  heuristics->income += sign;
96  break;
97  case ACCT_TYPE_EQUITY:
98  heuristics->equity += sign;
99  break;
100  default:
101  break;
102  }
103 }
104 
105 static SignReversals
106 heuristics_on_budget (GncBudget * budget, Account *root)
107 {
108  ProcessData heuristics = {0, 0, 0, 0, 0, gnc_budget_get_num_periods (budget),
109  budget};
110  SignReversals result;
111 
112  gnc_account_foreach_descendant (root, process_heuristics_acct, &heuristics);
113 
114  result =
115  heuristics.expense < 0 ? HEURISTICS_INC_EXP :
116  heuristics.income < 0 ? HEURISTICS_NONE :
117  HEURISTICS_CREDIT_ACC;
118 
119  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  return result;
125 }
126 
127 static void
128 fix_budget_acc_sign (Account *acc, gpointer user_data)
129 {
130  ReversalType* reversal = (ReversalType*) user_data;
131  GncBudget* budget = reversal->budget;
132  guint numperiods = gnc_budget_get_num_periods (budget);
134 
135  ENTER ("budget account reversal [%s] starting", xaccAccountGetName(acc));
136 
137  switch (reversal->policy)
138  {
139  case HEURISTICS_INC_EXP:
140  if ((type != ACCT_TYPE_INCOME) && (type != ACCT_TYPE_EXPENSE))
141  return;
142  PINFO ("budget account [%s] is inc/exp. reverse!",
143  xaccAccountGetName(acc));
144  break;
145  case HEURISTICS_CREDIT_ACC:
146  if ((type != ACCT_TYPE_LIABILITY) &&
147  (type != ACCT_TYPE_EQUITY) &&
148  (type != ACCT_TYPE_INCOME))
149  return;
150  PINFO ("budget account [%s] is credit-account. reverse!",
151  xaccAccountGetName(acc));
152  break;
153  default:
154  /* shouldn't happen. */
155  return;
156  }
157 
158  for (guint i=0; i < numperiods; ++i)
159  {
160  gnc_numeric amt;
161  if (!gnc_budget_is_account_period_value_set (budget, acc, i))
162  continue;
163 
164  amt = gnc_budget_get_account_period_value (budget, acc, i);
165  amt = gnc_numeric_neg (amt);
166  gnc_budget_set_account_period_value (budget, acc, i, amt);
167  }
168 
169  LEAVE ("budget account reversal [%s] completed!", xaccAccountGetName(acc));
170 }
171 
172 static void
173 maybe_scrub_budget (QofInstance* data, gpointer user_data)
174 {
175  GncBudget* budget = GNC_BUDGET(data);
176  Account *root = (Account*) user_data;
177  ReversalType reversal;
178 
179  reversal.policy = heuristics_on_budget (budget, root);
180  if (reversal.policy == HEURISTICS_NONE)
181  {
182  PWARN ("budget [%s] doesn't need reversing", gnc_budget_get_name (budget));
183  return;
184  }
185 
186  reversal.budget = budget;
187 
188  ENTER ("processing budget [%s] for reversal", gnc_budget_get_name (budget));
189  gnc_account_foreach_descendant (root, fix_budget_acc_sign, &reversal);
190  LEAVE ("completed budget [%s] for reversal", gnc_budget_get_name (budget));
191 }
192 
193 gboolean
194 gnc_maybe_scrub_all_budget_signs (QofBook *book)
195 {
196  QofCollection* collection = qof_book_get_collection (book, GNC_ID_BUDGET);
197  gboolean has_no_budgets = (qof_collection_count (collection) == 0);
198  gboolean featured = gnc_features_check_used (book, GNC_FEATURE_BUDGET_UNREVERSED);
199 
200  /* If there are no budgets, there shouldn't be feature! */
201  if (has_no_budgets && featured)
202  {
203  gnc_features_set_unused (book, GNC_FEATURE_BUDGET_UNREVERSED);
204  PWARN ("There are no budgets, removing feature BUDGET_UNREVERSED");
205  }
206 
207  if (has_no_budgets || featured)
208  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  qof_collection_foreach (collection, maybe_scrub_budget,
213  gnc_book_get_root_account (book));
214  gnc_features_set_used (book, GNC_FEATURE_BUDGET_UNREVERSED);
215  return TRUE;
216 }
217 /* ==================== END OF FILE ==================== */
GNCAccountType xaccAccountTypeGetFundamental(GNCAccountType t)
Convenience function to return the fundamental type asset/liability/income/expense/equity given an ac...
Definition: Account.cpp:4430
Expense accounts are used to denote expenses.
Definition: Account.h:143
#define PINFO(format, args...)
Print an informational note.
Definition: qoflog.h:256
GNCAccountType xaccAccountGetType(const Account *acc)
Returns the account&#39;s account type.
Definition: Account.cpp:3217
gnc_numeric gnc_numeric_neg(gnc_numeric a)
Returns a newly created gnc_numeric that is the negative of the given gnc_numeric value...
STRUCTS.
GnuCash Budgets.
void gnc_features_set_used(QofBook *book, const gchar *feature)
Indicate that the current book uses the given feature.
int gnc_numeric_compare(gnc_numeric a, gnc_numeric b)
Returns 1 if a>b, -1 if b>a, 0 if a == b.
gchar * gnc_numeric_to_string(gnc_numeric n)
Convert to string.
#define ENTER(format, args...)
Print a function entry debugging message.
Definition: qoflog.h:272
#define PWARN(format, args...)
Log a warning.
Definition: qoflog.h:250
Income accounts are used to denote income.
Definition: Account.h:140
void gnc_features_set_unused(QofBook *book, const gchar *feature)
Indicate that the current book does not use the given feature.
asset (and liability) accounts indicate generic, generalized accounts that are none of the above...
Definition: Account.h:116
Generic api to store and retrieve preferences.
liability (and asset) accounts indicate generic, generalized accounts that are none of the above...
Definition: Account.h:119
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition: qoflog.h:282
QofCollection * qof_book_get_collection(const QofBook *book, QofIdType entity_type)
Return The table of entities of the given type.
Definition: qofbook.cpp:521
guint qof_collection_count(const QofCollection *col)
return the number of entities in the collection.
Definition: qofid.cpp:244
const char * xaccAccountGetName(const Account *acc)
Get the account&#39;s name.
Definition: Account.cpp:3239
Equity account is used to balance the balance sheet.
Definition: Account.h:146
Utility functions for file access.