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
gnc_split_register_init_info(SplitRegister * reg)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 *
gnc_split_register_get_info(SplitRegister * reg)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 *
gnc_split_register_get_parent(SplitRegister * reg)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 *
gnc_split_register_get_split(SplitRegister * reg,VirtualCellLocation vcell_loc)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 *
gnc_split_register_get_default_account(SplitRegister * reg)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
gnc_split_register_set_template_account(SplitRegister * reg,Account * template_account)116 gnc_split_register_set_template_account (SplitRegister *reg,
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 *
gnc_split_register_get_trans(SplitRegister * reg,VirtualCellLocation vcell_loc)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 *
gnc_split_register_get_trans_split(SplitRegister * reg,VirtualCellLocation vcell_loc,VirtualCellLocation * trans_split_loc)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 *
gnc_split_register_get_current_trans_split(SplitRegister * reg,VirtualCellLocation * trans_split_loc)186 gnc_split_register_get_current_trans_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
gnc_split_register_find_split(SplitRegister * reg,Transaction * trans,Split * trans_split,Split * split,CursorClass find_class,VirtualCellLocation * vcell_loc)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
gnc_split_register_show_trans(SplitRegister * reg,VirtualCellLocation start_loc)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
gnc_split_register_set_trans_visible(SplitRegister * reg,VirtualCellLocation vcell_loc,gboolean visible,gboolean only_blank_split)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
gnc_split_register_set_cell_fractions(SplitRegister * reg,Split * split)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 &&
387                      !gnc_commodity_is_iso (xaccAccountGetCommodity(split_account)))
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, GNC_COMMODITY_MAX_FRACTION);
430 }
431 
432 CellBlock *
gnc_split_register_get_passive_cursor(SplitRegister * reg)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 *
gnc_split_register_get_active_cursor(SplitRegister * reg)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
gnc_split_register_set_last_num(SplitRegister * reg,const char * num)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
gnc_split_register_cursor_class(SplitRegister * reg,CellBlock * cursor)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 
523 CursorClass
gnc_split_register_get_cursor_class(SplitRegister * reg,VirtualCellLocation vcell_loc)524 gnc_split_register_get_cursor_class (SplitRegister *reg,
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 
544 CursorClass
gnc_split_register_get_current_cursor_class(SplitRegister * reg)545 gnc_split_register_get_current_cursor_class (SplitRegister *reg)
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 
559 CursorClass
gnc_split_register_cursor_name_to_class(const char * cursor_name)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 }
578