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