GnuCash  4.12-68-g5dc52459a1+
split-register-util.c
1 /********************************************************************\
2  * split-register-util.c -- split register utilities *
3  * *
4  * This program is free software; you can redistribute it and/or *
5  * modify it under the terms of the GNU General Public License as *
6  * published by the Free Software Foundation; either version 2 of *
7  * the License, or (at your option) any later version. *
8  * *
9  * This program is distributed in the hope that it will be useful, *
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12  * GNU General Public License for more details. *
13  * *
14  * You should have received a copy of the GNU General Public License*
15  * along with this program; if not, contact: *
16  * *
17  * Free Software Foundation Voice: +1-617-542-5942 *
18  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
19  * Boston, MA 02110-1301, USA gnu@gnu.org *
20  * *
21 \********************************************************************/
22 
23 #include <config.h>
24 
25 #include <glib.h>
26 
27 #include "gnc-date.h"
28 #include "pricecell.h"
29 #include "split-register-p.h"
30 
31 
32 static QofLogModule log_module = GNC_MOD_LEDGER;
33 
34 
35 /* The routines below create, access, and destroy the SRInfo structure
36  * used by SplitLedger routines to store data for a particular register.
37  * This is the only code that should access the user_data member of a
38  * SplitRegister directly. If additional user data is needed, just add
39  * it to the SRInfo structure above. */
40 static void
41 gnc_split_register_init_info (SplitRegister *reg)
42 {
43  SRInfo *info;
44 
45  if (reg == NULL)
46  return;
47 
48  info = g_new0 (SRInfo, 1);
49 
50  info->blank_split_guid = *guid_null ();
51  info->pending_trans_guid = *guid_null ();
52  info->default_account = *guid_null ();
53  info->template_account = *guid_null ();
54 
55  info->last_date_entered = gnc_time64_get_today_start ();
56 
57  info->first_pass = TRUE;
58  info->full_refresh = TRUE;
59  info->separator_changed = TRUE;
60 
61  reg->sr_info = info;
62 }
63 
64 SRInfo *
65 gnc_split_register_get_info (SplitRegister *reg)
66 {
67  if (!reg)
68  return NULL;
69 
70  if (reg->sr_info == NULL)
71  gnc_split_register_init_info (reg);
72 
73  return reg->sr_info;
74 }
75 
76 GtkWidget *
77 gnc_split_register_get_parent (SplitRegister *reg)
78 {
79  SRInfo *info = gnc_split_register_get_info (reg);
80 
81  if (reg == NULL)
82  return NULL;
83 
84  if (info->get_parent == NULL)
85  return NULL;
86 
87  return info->get_parent (info->user_data);
88 }
89 
90 Split *
91 gnc_split_register_get_split (SplitRegister *reg,
92  VirtualCellLocation vcell_loc)
93 {
94  GncGUID *guid;
95 
96  if (reg == NULL)
97  return NULL;
98 
99  guid = gnc_table_get_vcell_data (reg->table, vcell_loc);
100  if (guid == NULL)
101  return NULL;
102 
103  return xaccSplitLookup (guid, gnc_get_current_book ());
104 }
105 
106 Account *
107 gnc_split_register_get_default_account (SplitRegister *reg)
108 {
109  SRInfo *info = gnc_split_register_get_info (reg);
110 
111  return xaccAccountLookup (&info->default_account,
112  gnc_get_current_book ());
113 }
114 
115 void
117  Account *template_account)
118 {
119  SRInfo *info = gnc_split_register_get_info (reg);
120 
121  g_return_if_fail (reg != NULL);
122 
123  info->template_account = *xaccAccountGetGUID (template_account);
124 }
125 
126 Transaction *
127 gnc_split_register_get_trans (SplitRegister *reg,
128  VirtualCellLocation vcell_loc)
129 {
130  Split *split;
131 
132  if (!reg || !reg->table)
133  return NULL;
134 
135  split = gnc_split_register_get_split (reg, vcell_loc);
136 
137  if (split != NULL)
138  return xaccSplitGetParent(split);
139 
140  /* Split is blank. Assume it is the blank split of a multi-line
141  * transaction. Go back one row to find a split in the transaction. */
142  vcell_loc.virt_row--;
143 
144  split = gnc_split_register_get_split (reg, vcell_loc);
145 
146  /* This split could be NULL during register initialization. */
147  if (split == NULL)
148  return NULL;
149 
150  return xaccSplitGetParent(split);
151 }
152 
153 Split *
154 gnc_split_register_get_trans_split (SplitRegister *reg,
155  VirtualCellLocation vcell_loc,
156  VirtualCellLocation *trans_split_loc)
157 {
158  CursorClass cursor_class;
159 
160  if (reg == NULL)
161  return NULL;
162 
163  while (TRUE)
164  {
165  if ((0 > vcell_loc.virt_row) || (0 > vcell_loc.virt_col))
166  {
167  PERR ("bad row\n");
168  return NULL;
169  }
170 
171  cursor_class = gnc_split_register_get_cursor_class (reg, vcell_loc);
172 
173  if (cursor_class == CURSOR_CLASS_TRANS)
174  {
175  if (trans_split_loc)
176  *trans_split_loc = vcell_loc;
177 
178  return gnc_split_register_get_split (reg, vcell_loc);
179  }
180 
181  vcell_loc.virt_row--;
182  }
183 }
184 
185 Split *
187  SplitRegister *reg, VirtualCellLocation *trans_split_loc)
188 {
189  VirtualCellLocation vcell_loc;
190 
191  if (reg == NULL)
192  return NULL;
193 
194  vcell_loc = reg->table->current_cursor_loc.vcell_loc;
195 
196  return gnc_split_register_get_trans_split (reg, vcell_loc, trans_split_loc);
197 }
198 
199 gboolean
200 gnc_split_register_find_split (SplitRegister *reg,
201  Transaction *trans, Split *trans_split,
202  Split *split, CursorClass find_class,
203  VirtualCellLocation *vcell_loc)
204 {
205  Table *table = reg->table;
206  gboolean found_trans = FALSE;
207  gboolean found_trans_split = FALSE;
208  gboolean found_something = FALSE;
209  CursorClass cursor_class;
210  int v_row, v_col;
211  Transaction *t;
212  Split *s;
213 
214  for (v_row = 1; v_row < table->num_virt_rows; v_row++)
215  for (v_col = 0; v_col < table->num_virt_cols; v_col++)
216  {
217  VirtualCellLocation vc_loc = { v_row, v_col };
218 
219  s = gnc_split_register_get_split (reg, vc_loc);
220  t = xaccSplitGetParent(s);
221 
222  cursor_class = gnc_split_register_get_cursor_class (reg, vc_loc);
223 
224  if (t == trans)
225  {
226  /* A register entry for the correct transaction. */
227  found_trans = TRUE;
228 
229  if (cursor_class == CURSOR_CLASS_TRANS)
230  {
231  /* This row is the transaction split for this transaction. */
232  if (s == trans_split)
233  /* It's the copy of the transaction that we want. */
234  found_trans_split = TRUE;
235  else
236  found_trans_split = FALSE;
237 
238  if (find_class == CURSOR_CLASS_TRANS &&
239  (s == split || reg->style == REG_STYLE_JOURNAL))
240  {
241  /* We're looking for a transaction split and this is the split we're looking for
242  or there is only one entry for this transaction in this register (since it's
243  a journal style register) so we must return the only transaction split there is. */
244  if (vcell_loc != NULL)
245  *vcell_loc = vc_loc;
246  return TRUE;
247  }
248  }
249  }
250  else
251  {
252  /* Not the correct transaction. We shouldn't get here if these are true, but just
253  to be safe reset them. */
254  found_trans = FALSE;
255  found_trans_split = FALSE;
256  }
257 
258  if (found_trans && (s == split) && s)
259  {
260  /* We're on the right transaction, but perhaps not the copy of it we want, and
261  this is the correct split, return it if we don't find anything better. */
262  if (vcell_loc != NULL)
263  *vcell_loc = vc_loc;
264 
265  found_something = TRUE;
266  }
267 
268  if (found_trans_split && (s == split))
269  {
270  /* We're on the right copy of the right transaction, and this is the split we
271  want, return it (it should be the right class since if we wanted a transaction
272  split we would have returned it above. */
273  if (vcell_loc != NULL)
274  *vcell_loc = vc_loc;
275 
276  if (cursor_class == find_class)
277  return TRUE;
278  }
279  }
280 
281  return found_something;
282 }
283 
284 void
285 gnc_split_register_show_trans (SplitRegister *reg,
286  VirtualCellLocation start_loc)
287 {
288  VirtualCellLocation end_loc;
289  int v_row;
290 
291  end_loc = start_loc;
292 
293  for (v_row = end_loc.virt_row + 1;
294  v_row < reg->table->num_virt_rows; v_row++)
295  {
296  VirtualCellLocation vc_loc = { v_row, 0 };
297  CursorClass cursor_class;
298 
299  cursor_class = gnc_split_register_get_cursor_class (reg, vc_loc);
300  if (cursor_class == CURSOR_CLASS_TRANS)
301  break;
302 
303  if (cursor_class != CURSOR_CLASS_SPLIT)
304  {
305  v_row--;
306  break;
307  }
308  }
309 
310  end_loc.virt_row = MIN (v_row, reg->table->num_virt_rows - 1);
311 
312  gnc_table_show_range (reg->table, start_loc, end_loc);
313 }
314 
315 void
317  VirtualCellLocation vcell_loc,
318  gboolean visible,
319  gboolean only_blank_split)
320 {
321  CursorClass cursor_class;
322 
323  while (TRUE)
324  {
325  vcell_loc.virt_row++;
326 
327  cursor_class = gnc_split_register_get_cursor_class (reg, vcell_loc);
328  if (cursor_class != CURSOR_CLASS_SPLIT)
329  return;
330 
331  if (only_blank_split && gnc_split_register_get_split (reg, vcell_loc))
332  continue;
333 
334  gnc_table_set_virt_cell_visible (reg->table, vcell_loc, visible);
335  }
336 }
337 
338 void
339 gnc_split_register_set_cell_fractions (SplitRegister *reg, Split *split)
340 {
341  Account *split_account;
342  Account *reg_account;
343  Transaction *trans;
344  gnc_commodity *trans_currency; /* or default currency if no transaction */
345  PriceCell *cell;
346  int fraction;
347  gboolean trading_accts;
348  gnc_commodity *commodity;
349 
350  /* This function must use the same algorithm as gnc_split_register_get_shares_entry
351  and gnc_split_register_get_debcred_entry. Changes here may require changes in
352  one of them or vice versa. */
353 
354  /* If the split has a new account use that, otherwise use the one it
355  had before we started editing it. */
356  split_account = gnc_split_register_get_account (reg, XFRM_CELL);
357  if (!split_account)
358  split_account = xaccSplitGetAccount (split);
359 
360  reg_account = gnc_split_register_get_default_account (reg);
361 
362  trans = xaccSplitGetParent (split);
363  if (trans)
364  {
365  trading_accts = xaccTransUseTradingAccounts (trans);
366  trans_currency = xaccTransGetCurrency (trans);
367  }
368  else
369  {
370  /* It should be ok to use the current book since that's what
371  gnc_split_register_get_account uses to find the account. */
372  trading_accts = qof_book_use_trading_accounts (gnc_get_current_book());
373  trans_currency = gnc_default_currency();
374  }
375 
376  /* What follows is similar to the tests in gnc_split_register_get_debcred_entry */
377  if (trading_accts)
378  {
379  if (reg->type == STOCK_REGISTER ||
380  reg->type == CURRENCY_REGISTER ||
381  reg->type == PORTFOLIO_LEDGER)
382  {
383  /* These tests are similar to gnc_split_register_use_security_cells */
384  if (!split_account)
385  commodity = trans_currency;
386  else if (trading_accts &&
388  commodity = trans_currency;
389  else if (xaccAccountIsPriced (split_account))
390  commodity = trans_currency;
391  else
392  commodity = xaccAccountGetCommodity (split_account);
393  }
394  else
395  {
396  commodity = xaccAccountGetCommodity (split_account);
397  }
398  }
399  else
400  {
401  /* Not trading accounts */
402  if (reg->type == STOCK_REGISTER ||
403  reg->type == CURRENCY_REGISTER ||
404  reg->type == PORTFOLIO_LEDGER)
405  commodity = trans_currency;
406  else
407  commodity = xaccAccountGetCommodity (reg_account);
408  }
409  if (!commodity)
410  commodity = gnc_default_currency ();
411 
412  fraction = gnc_commodity_get_fraction (commodity);
413 
414  cell = (PriceCell *) gnc_table_layout_get_cell (reg->table->layout,
415  DEBT_CELL);
416  gnc_price_cell_set_fraction (cell, fraction);
417 
418  cell = (PriceCell *) gnc_table_layout_get_cell (reg->table->layout,
419  CRED_CELL);
420  gnc_price_cell_set_fraction (cell, fraction);
421 
422  cell = (PriceCell *) gnc_table_layout_get_cell (reg->table->layout,
423  SHRS_CELL);
424 
425  /* gnc_split_register_get_shares_entry always uses the split's commodity */
426  if (split_account)
428  else
430 }
431 
432 CellBlock *
433 gnc_split_register_get_passive_cursor (SplitRegister *reg)
434 {
435  const char *cursor_name = NULL;
436 
437  switch (reg->style)
438  {
439  case REG_STYLE_LEDGER:
440  case REG_STYLE_AUTO_LEDGER:
441  cursor_name = reg->use_double_line ?
442  (reg->use_tran_num_for_num_field ? CURSOR_DOUBLE_LEDGER
443  : CURSOR_DOUBLE_LEDGER_NUM_ACTN)
444  : CURSOR_SINGLE_LEDGER;
445  break;
446 
447  case REG_STYLE_JOURNAL:
448  cursor_name = reg->use_double_line ?
449  (reg->use_tran_num_for_num_field ? CURSOR_DOUBLE_JOURNAL
450  : CURSOR_DOUBLE_JOURNAL_NUM_ACTN)
451  : CURSOR_SINGLE_JOURNAL;
452  break;
453  }
454 
455  if (!cursor_name)
456  {
457  PWARN ("bad register style");
458  return NULL;
459  }
460 
461  return gnc_table_layout_get_cursor (reg->table->layout, cursor_name);
462 }
463 
464 CellBlock *
465 gnc_split_register_get_active_cursor (SplitRegister *reg)
466 {
467  SRInfo *info = gnc_split_register_get_info (reg);
468  const char *cursor_name = NULL;
469 
470  switch (reg->style)
471  {
472  case REG_STYLE_LEDGER:
473  if (!info->trans_expanded)
474  {
475  cursor_name = reg->use_double_line ?
476  (reg->use_tran_num_for_num_field ? CURSOR_DOUBLE_LEDGER
477  : CURSOR_DOUBLE_LEDGER_NUM_ACTN)
478  : CURSOR_SINGLE_LEDGER;
479  break;
480  }
481 
482  /* fall through */
483  case REG_STYLE_AUTO_LEDGER:
484  case REG_STYLE_JOURNAL:
485  cursor_name = reg->use_double_line ?
486  (reg->use_tran_num_for_num_field ? CURSOR_DOUBLE_JOURNAL
487  : CURSOR_DOUBLE_JOURNAL_NUM_ACTN)
488  : CURSOR_SINGLE_JOURNAL;
489  break;
490  }
491 
492  if (!cursor_name)
493  {
494  PWARN ("bad register style");
495  return NULL;
496  }
497 
498  return gnc_table_layout_get_cursor (reg->table->layout, cursor_name);
499 }
500 
501 void
502 gnc_split_register_set_last_num (SplitRegister *reg, const char *num)
503 {
504  Account *account;
505 
506  account = gnc_split_register_get_default_account (reg);
507  if (!account)
508  return;
509 
510  xaccAccountSetLastNum (account, num);
511 }
512 
513 static CursorClass
514 gnc_split_register_cursor_class (SplitRegister *reg,
515  CellBlock *cursor)
516 {
517  if (cursor == NULL)
518  return CURSOR_CLASS_NONE;
519 
520  return gnc_split_register_cursor_name_to_class (cursor->cursor_name);
521 }
522 
525  VirtualCellLocation vcell_loc)
526 {
527  VirtualCell *vcell;
528  Table *table;
529 
530  if (reg == NULL)
531  return CURSOR_CLASS_NONE;
532 
533  table = reg->table;
534  if (table == NULL)
535  return CURSOR_CLASS_NONE;
536 
537  vcell = gnc_table_get_virtual_cell (table, vcell_loc);
538  if (vcell == NULL)
539  return CURSOR_CLASS_NONE;
540 
541  return gnc_split_register_cursor_class (reg, vcell->cellblock);
542 }
543 
546 {
547  Table *table;
548 
549  if (reg == NULL)
550  return CURSOR_CLASS_NONE;
551 
552  table = reg->table;
553  if (table == NULL)
554  return CURSOR_CLASS_NONE;
555 
556  return gnc_split_register_cursor_class (reg, table->current_cursor);
557 }
558 
560 gnc_split_register_cursor_name_to_class (const char *cursor_name)
561 {
562  if (cursor_name == NULL)
563  return CURSOR_CLASS_NONE;
564 
565  if (strcmp (cursor_name, CURSOR_SINGLE_LEDGER) == 0 ||
566  strcmp (cursor_name, CURSOR_DOUBLE_LEDGER) == 0 ||
567  strcmp (cursor_name, CURSOR_DOUBLE_LEDGER_NUM_ACTN) == 0 ||
568  strcmp (cursor_name, CURSOR_SINGLE_JOURNAL) == 0 ||
569  strcmp (cursor_name, CURSOR_DOUBLE_JOURNAL) == 0 ||
570  strcmp (cursor_name, CURSOR_DOUBLE_JOURNAL_NUM_ACTN) == 0)
571  return CURSOR_CLASS_TRANS;
572 
573  if (strcmp (cursor_name, CURSOR_SPLIT) == 0)
574  return CURSOR_CLASS_SPLIT;
575 
576  return CURSOR_CLASS_NONE;
577 }
CursorClass gnc_split_register_get_current_cursor_class(SplitRegister *reg)
Returns the class of a register&#39;s current cursor.
Split * gnc_split_register_get_current_trans_split(SplitRegister *reg, VirtualCellLocation *trans_split_loc)
Gets the anchoring split of the transaction at the current cursor location, which may be on the trans...
int gnc_commodity_get_fraction(const gnc_commodity *cm)
Retrieve the fraction for the specified commodity.
gboolean xaccTransUseTradingAccounts(const Transaction *trans)
Determine whether this transaction should use commodity trading accounts.
Definition: Transaction.c:1034
Date and Time handling routines.
#define GNC_COMMODITY_MAX_FRACTION
Max fraction is 10^9 because 10^10 would require changing it to an int64_t.
gboolean xaccAccountIsPriced(const Account *acc)
Returns true if the account is a stock, mutual fund or currency, otherwise false. ...
Definition: Account.cpp:4712
void gnc_split_register_set_trans_visible(SplitRegister *reg, VirtualCellLocation vcell_loc, gboolean visible, gboolean only_blank_split)
Set the visibility of the split rows belonging to a transaction located at vcell_loc.
gpointer gnc_table_get_vcell_data(Table *table, VirtualCellLocation vcell_loc)
returns the virtual cell data associated with a cursor located at the given virtual coords...
Definition: table-allgui.c:932
void gnc_table_set_virt_cell_visible(Table *table, VirtualCellLocation vcell_loc, gboolean visible)
Set the visibility flag for a particular location.
Definition: table-allgui.c:720
int xaccAccountGetCommoditySCU(const Account *acc)
Return the SCU for the account.
Definition: Account.cpp:2701
holds information about each virtual cell.
Definition: table-allgui.h:132
CursorClass gnc_split_register_get_cursor_class(SplitRegister *reg, VirtualCellLocation vcell_loc)
Returns the class of the cursor at the given virtual cell location.
void gnc_split_register_set_template_account(SplitRegister *reg, Account *template_account)
Set the template account for use in a template register.
Transaction * xaccSplitGetParent(const Split *split)
Returns the parent transaction of the split.
#define PERR(format, args...)
Log a serious error.
Definition: qoflog.h:244
void xaccAccountSetLastNum(Account *acc, const char *num)
Set the last num field of an Account.
Definition: Account.cpp:4945
gnc_commodity * gnc_default_currency(void)
Return the default currency set by the user.
Definition: gnc-ui-util.c:1197
#define PWARN(format, args...)
Log a warning.
Definition: qoflog.h:250
#define xaccAccountGetGUID(X)
Definition: Account.h:248
Split * xaccSplitLookup(const GncGUID *guid, QofBook *book)
The xaccSplitLookup() subroutine will return the split associated with the given id, or NULL if there is no such split.
Definition: Split.c:1072
CursorClass
Types of cursors.
The PriceCell object implements a cell handler that stores a single double-precision value...
Definition: pricecell.h:50
void gnc_table_show_range(Table *table, VirtualCellLocation start_loc, VirtualCellLocation end_loc)
Try to show the whole range in the register.
Definition: table-gnome.c:215
VirtualCell * gnc_table_get_virtual_cell(Table *table, VirtualCellLocation vcell_loc)
returns the virtual cell associated with a particular virtual location.
Definition: table-allgui.c:227
private declarations for SplitRegister
time64 gnc_time64_get_today_start(void)
The gnc_time64_get_today_start() routine returns a time64 value corresponding to the first second of ...
Definition: gnc-date.cpp:1350
void gnc_price_cell_set_fraction(PriceCell *cell, int fraction)
Sets the fraction used for rounding.
Definition: pricecell.c:242
Account * xaccSplitGetAccount(const Split *split)
Returns the account of this split, which was set through xaccAccountInsertSplit().
Definition: gmock-Split.cpp:53
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Get the account&#39;s commodity.
Definition: Account.cpp:3448
const GncGUID * guid_null(void)
Returns a GncGUID which is guaranteed to never reference any entity.
Definition: guid.cpp:131
gnc_commodity * xaccTransGetCurrency(const Transaction *trans)
Returns the valuation commodity of this transaction.
Definition: Transaction.c:1367
The type used to store guids in C.
Definition: guid.h:75
gboolean qof_book_use_trading_accounts(const QofBook *book)
Returns flag indicating whether this book uses trading accounts.
Definition: qofbook.cpp:1035
gboolean gnc_commodity_is_iso(const gnc_commodity *cm)
Checks to see if the specified commodity is an ISO 4217 recognized currency.
Account * xaccAccountLookup(const GncGUID *guid, QofBook *book)
The xaccAccountLookup() subroutine will return the account associated with the given id...
Definition: Account.cpp:2050