GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
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 [email protected] *
20  * *
21 \********************************************************************/
22 
23 #include "config.h"
24 
25 #include <glib.h>
26 
27 #include <gnc-gdate-utils.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
316 gnc_split_register_set_trans_visible (SplitRegister *reg,
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)
427  gnc_price_cell_set_fraction (cell, xaccAccountGetCommoditySCU (split_account));
428  else
429  gnc_price_cell_set_fraction (cell, 1000000);
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 }
time64 gnc_time64_get_today_start(void)
int gnc_commodity_get_fraction(const gnc_commodity *cm)
gboolean xaccTransUseTradingAccounts(const Transaction *trans)
Definition: Transaction.c:1015
gboolean xaccAccountIsPriced(const Account *acc)
Definition: Account.c:4249
The type, style and table for the register.
gboolean use_tran_num_for_num_field
int xaccAccountGetCommoditySCU(const Account *acc)
Definition: Account.c:2458
CursorClass gnc_split_register_get_cursor_class(SplitRegister *reg, VirtualCellLocation vcell_loc)
Split * gnc_split_register_get_current_trans_split(SplitRegister *reg, VirtualCellLocation *trans_split_loc)
Transaction * xaccSplitGetParent(const Split *split)
Definition: Split.c:1903
#define PERR(format, args...)
Definition: qoflog.h:237
void xaccAccountSetLastNum(Account *acc, const char *num)
Definition: Account.c:4481
Definition: guid.h:65
gnc_commodity * gnc_default_currency(void)
Definition: gnc-ui-util.c:939
#define PWARN(format, args...)
Definition: qoflog.h:243
gboolean use_double_line
#define xaccAccountGetGUID(X)
Definition: Account.h:239
Split * xaccSplitLookup(const GncGUID *guid, QofBook *book)
Definition: Split.c:1104
void gnc_split_register_set_template_account(SplitRegister *reg, Account *template_account)
CursorClass
GDate helper routines.
Definition: SplitP.h:71
CursorClass gnc_split_register_get_current_cursor_class(SplitRegister *reg)
Account * xaccSplitGetAccount(const Split *s)
Definition: Split.c:968
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Definition: Account.c:3148
const GncGUID * guid_null(void)
gnc_commodity * xaccTransGetCurrency(const Transaction *trans)
Definition: Transaction.c:1348
gboolean qof_book_use_trading_accounts(const QofBook *book)
gboolean gnc_commodity_is_iso(const gnc_commodity *cm)
const gchar * QofLogModule
Definition: qofid.h:89
Account * xaccAccountLookup(const GncGUID *guid, QofBook *book)
Definition: Account.c:1827