GnuCash c935c2f+
Loading...
Searching...
No Matches
gncEntryLedger.c
1/*
2 * gncEntryLedger.c -- a Ledger widget for entering GncEntry objects
3 * Copyright (C) 2001, 2002, 2003 Derek Atkins
4 * Author: Derek Atkins <warlord@MIT.EDU>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of
9 * the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, contact:
18 *
19 * Free Software Foundation Voice: +1-617-542-5942
20 * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
21 * Boston, MA 02110-1301, USA gnu@gnu.org
22 */
23
24#include <config.h>
25
26#include <gtk/gtk.h>
27#include <glib/gi18n.h>
28
29#include "Account.h"
30#include "dialog-account.h"
31#include "dialog-utils.h"
32#include "gnc-ui-util.h"
33#include "combocell.h"
34#include "pricecell.h"
35#include "recncell.h"
36#include "checkboxcell.h"
37
38#include "gnc-component-manager.h"
39#include "gnc-ui.h"
40#include "gnc-warnings.h"
41
42#include "gncEntry.h"
43#include "gncEntryLedger.h"
44#include "gncEntryLedgerP.h"
46#include "gncEntryLedgerModel.h"
48
49static QofLogModule log_module = "Business Entry Ledger";
50
53static void
54gnc_entry_ledger_clear_blank_entry (GncEntryLedger *ledger)
55{
56 GncEntry *entry;
57
58 if (!ledger) return;
59
60 entry = gnc_entry_ledger_get_blank_entry (ledger);
61 if (entry)
62 {
63 if (!gncEntryIsOpen (entry))
64 gncEntryBeginEdit (entry);
65 gncEntryDestroy (entry);
66 }
67
68 ledger->blank_entry_guid = *guid_null ();
69 ledger->blank_entry_edited = FALSE;
70}
71
74GncEntry *
75gnc_entry_ledger_get_blank_entry (GncEntryLedger *ledger)
76{
77 if (!ledger) return NULL;
78 return gncEntryLookup (ledger->book, &(ledger->blank_entry_guid));
79}
80
81Account *
82gnc_entry_ledger_get_account_by_name (GncEntryLedger *ledger, BasicCell * bcell,
83 const char *name, gboolean *isnew)
84{
85 const char *placeholder = _("The account %s does not allow transactions.");
86 const char *missing = _("The account %s does not exist. "
87 "Would you like to create it?");
88 char *account_name;
89 ComboCell *cell = (ComboCell *) bcell;
90 Account *account;
91 GList *account_types = NULL;
92
93 /* Find the account */
94 account = gnc_account_lookup_for_register (gnc_get_current_root_account (), name);
95 if (!account)
96 account = gnc_account_lookup_by_code (gnc_get_current_root_account(), name);
97
98 if (!account)
99 {
100 /* Ask if they want to create a new one. */
101 if (!gnc_verify_dialog (GTK_WINDOW (ledger->parent), TRUE, missing, name))
102 return NULL;
103
104 /* No changes, as yet. */
105 *isnew = FALSE;
106
107 /* User said yes, they want to create a new account. */
108 account_types = g_list_prepend (account_types, (gpointer)ACCT_TYPE_CREDIT);
109 account_types = g_list_prepend (account_types, (gpointer)ACCT_TYPE_ASSET);
110 account_types = g_list_prepend (account_types, (gpointer)ACCT_TYPE_LIABILITY);
111 if ( ledger->is_cust_doc )
112 account_types = g_list_prepend (account_types, (gpointer)ACCT_TYPE_INCOME);
113 else
114 account_types = g_list_prepend (account_types, (gpointer)ACCT_TYPE_EXPENSE);
115
116 account = gnc_ui_new_accounts_from_name_with_defaults (GTK_WINDOW (ledger->parent), name, account_types,
117 NULL, NULL);
118 g_list_free ( account_types );
119 if (!account)
120 return NULL;
121 *isnew = TRUE;
122 }
123
124 /* Now have a new account. Update the cell with the name as created. */
125 account_name = gnc_get_account_name_for_register (account);
126 if (g_strcmp0(account_name, gnc_basic_cell_get_value(bcell)))
127 {
128 gnc_combo_cell_set_value (cell, account_name);
129 gnc_basic_cell_set_changed (&cell->cell, TRUE);
130 }
131 g_free (account_name);
132
133 /* See if the account (either old or new) is a placeholder. */
134 if (xaccAccountGetPlaceholder (account))
135 {
136 gnc_error_dialog (GTK_WINDOW (ledger->parent), placeholder, name);
137 }
138
139 /* Be seeing you. */
140 return account;
141}
142
143Account * gnc_entry_ledger_get_account (GncEntryLedger *ledger,
144 const char * cell_name)
145{
146 BasicCell *cell;
147 const char * name;
148 gboolean dummy;
149
150 cell = gnc_table_layout_get_cell (ledger->table->layout, cell_name);
151 if (!cell)
152 return NULL;
153 name = gnc_basic_cell_get_value (cell);
154 return gnc_entry_ledger_get_account_by_name (ledger, cell, name, &dummy);
155}
156
157GncTaxTable * gnc_entry_ledger_get_taxtable (GncEntryLedger *ledger,
158 const char *cell_name)
159{
160 GncEntry *entry;
161 const char * name;
162
163 /* If the cursor has changed, then pull in the current table */
164 if (gnc_table_layout_get_cell_changed (ledger->table->layout,
165 cell_name, TRUE))
166 {
167 name = gnc_table_layout_get_cell_value (ledger->table->layout, cell_name);
168 return gncTaxTableLookupByName (ledger->book, name);
169 }
170
171 /* If it has not changed, pull in the table from the entry */
172 entry = gnc_entry_ledger_get_current_entry (ledger);
173 if (ledger->is_cust_doc)
174 return gncEntryGetInvTaxTable (entry);
175 else
176 return gncEntryGetBillTaxTable (entry);
177}
178
179gboolean gnc_entry_ledger_get_checkmark (GncEntryLedger *ledger,
180 const char * cell_name)
181{
182 CheckboxCell *cell =
183 (CheckboxCell *) gnc_table_layout_get_cell (ledger->table->layout, cell_name);
184
185 if (!cell)
186 return FALSE;
187
188 return cell->flag;
189}
190
191gint gnc_entry_ledger_get_type (GncEntryLedger *ledger, const char * cell_name)
192{
193 RecnCell *cell =
194 (RecnCell *) gnc_table_layout_get_cell (ledger->table->layout, cell_name);
195
196 if (!cell)
197 return -1;
198
199 return (gnc_recn_cell_get_flag (cell) - '0');
200}
201
202/* Return TRUE if value is valid, return FALSE if invalid */
203gboolean gnc_entry_ledger_get_numeric (GncEntryLedger *ledger,
204 const char *cell_name,
205 gnc_numeric *value)
206{
207 PriceCell *cell =
208 (PriceCell *) gnc_table_layout_get_cell (ledger->table->layout, cell_name);
209
210 if (!value || !cell)
211 return FALSE;
212
213 *value = gnc_price_cell_get_value (cell);
214 return TRUE;
215}
216
217
218GncEntry * gnc_entry_ledger_get_entry (GncEntryLedger *ledger,
219 VirtualCellLocation vcell_loc)
220{
221 GncGUID *guid;
222
223 if (!ledger) return NULL;
224
225 guid = gnc_table_get_vcell_data (ledger->table, vcell_loc);
226 if (!guid) return NULL;
227
228 return gncEntryLookup (ledger->book, guid);
229}
230
231/* Returns the Entry where the cursor is currently located. */
232GncEntry * gnc_entry_ledger_get_current_entry (GncEntryLedger *ledger)
233{
234 if (!ledger) return NULL;
235
236 return
238 ledger->table->current_cursor_loc.vcell_loc);
239}
240
241static void gnc_entry_ledger_config_action (GncEntryLedger *ledger)
242{
243 ComboCell *cell;
244
245 cell = (ComboCell *) gnc_table_layout_get_cell (ledger->table->layout,
246 ENTRY_ACTN_CELL);
247 if (!cell) return;
248
249 /* XXX: change this based on the ledger type */
250
251 gnc_combo_cell_add_menu_item (cell, _("Hours"));
252 gnc_combo_cell_add_menu_item (cell, _("Project"));
253 gnc_combo_cell_add_menu_item (cell, _("Material"));
254}
255
256static void
257gnc_entry_ledger_config_cells (GncEntryLedger *ledger)
258{
259 /* the action cell */
261 ((ComboCell *)
262 gnc_table_layout_get_cell (ledger->table->layout, ENTRY_ACTN_CELL), TRUE);
263
264 /* The action cell should also accept strings not in the list */
266 ((ComboCell *)
267 gnc_table_layout_get_cell (ledger->table->layout, ENTRY_ACTN_CELL), FALSE);
268
269 /* add menu items for the action and payment cells */
270 gnc_entry_ledger_config_action (ledger);
271}
272
273/* Create and return a new GncEntry Ledger */
274GncEntryLedger * gnc_entry_ledger_new (QofBook *book, GncEntryLedgerType type)
275{
276 GncEntryLedger *ledger;
277
278 if (!book) return NULL;
279 if (type < 0 || type >= GNCENTRY_NUM_REGISTER_TYPES) return NULL;
280
281 ledger = g_new0 (GncEntryLedger, 1);
282 ledger->type = type;
283 ledger->book = book;
284 ledger->traverse_to_new = TRUE;
285 ledger->prefs_group = NULL;
286
287 /* Orders and Invoices are "invoices" for lookups */
288 switch (type)
289 {
290 case GNCENTRY_ORDER_ENTRY:
291 case GNCENTRY_ORDER_VIEWER:
292 case GNCENTRY_INVOICE_ENTRY:
293 case GNCENTRY_INVOICE_VIEWER:
294 ledger->is_cust_doc = TRUE;
295 ledger->is_credit_note = FALSE;
296 break;
297 case GNCENTRY_BILL_ENTRY:
298 case GNCENTRY_BILL_VIEWER:
299 case GNCENTRY_EXPVOUCHER_ENTRY:
300 case GNCENTRY_EXPVOUCHER_VIEWER:
301 case GNCENTRY_NUM_REGISTER_TYPES:
302 ledger->is_cust_doc = FALSE;
303 ledger->is_credit_note = FALSE;
304 break;
305 case GNCENTRY_CUST_CREDIT_NOTE_ENTRY:
306 case GNCENTRY_CUST_CREDIT_NOTE_VIEWER:
307 ledger->is_cust_doc = TRUE;
308 ledger->is_credit_note = TRUE;
309 break;
310 case GNCENTRY_VEND_CREDIT_NOTE_ENTRY:
311 case GNCENTRY_VEND_CREDIT_NOTE_VIEWER:
312 case GNCENTRY_EMPL_CREDIT_NOTE_ENTRY:
313 case GNCENTRY_EMPL_CREDIT_NOTE_VIEWER:
314 ledger->is_cust_doc = FALSE;
315 ledger->is_credit_note = TRUE;
316 break;
317 default:
318 PWARN ("Bad GncEntryLedgerType");
319 g_free (ledger);
320 return NULL;
321 break;
322 }
323
324 ledger->blank_entry_guid = *guid_null();
325 ledger->blank_entry_edited = FALSE;
326
327 {
328 GDate *today = gnc_g_date_new_today();
329 ledger->last_date_entered = *today;
330 g_date_free(today);
331 }
332
333 {
334 TableLayout *layout = gnc_entry_ledger_layout_new (ledger);
336 TableControl *control = gnc_entry_ledger_control_new ();
337 model->handler_user_data = ledger;
338 control->user_data = ledger;
339
340 ledger->table = gnc_table_new (layout, model, control);
341 }
342
343 gnc_entry_ledger_config_cells (ledger);
344
345 /* set up header */
346 {
347 VirtualCellLocation vcell_loc = { 0, 0 };
348 CellBlock *header;
349
350 header = gnc_table_layout_get_cursor (ledger->table->layout, CURSOR_HEADER);
351
352 gnc_table_set_vcell (ledger->table, header, NULL, TRUE, TRUE, vcell_loc);
353 }
354
355 /* set up first initial row */
356 {
357 VirtualLocation vloc;
358 CellBlock *cursor;
359
360 vloc.vcell_loc.virt_row = 1;
361 vloc.vcell_loc.virt_col = 0;
362 vloc.phys_row_offset = 0;
363 vloc.phys_col_offset = 0;
364
365 cursor = gnc_table_layout_get_cursor (ledger->table->layout, "cursor");
366
367 gnc_table_set_vcell (ledger->table, cursor, NULL, TRUE, TRUE, vloc.vcell_loc);
368
369 if (gnc_table_find_close_valid_cell (ledger->table, &vloc, FALSE))
370 gnc_table_move_cursor (ledger->table, vloc);
371 else
372 {
373 g_warning ("Can't find valid initial location");
374 }
375 }
376
377 /* Initialize Display */
378 gnc_entry_ledger_display_init (ledger);
379 if (qof_book_is_readonly(ledger->book))
380 {
381 gnc_entry_ledger_set_readonly(ledger, TRUE);
382 }
383 return ledger;
384}
385
386/* Destroy the GncEntry Ledger */
387void gnc_entry_ledger_destroy (GncEntryLedger *ledger)
388{
389 if (!ledger) return;
390
391 /* Destroy blank entry, etc. */
392 gnc_entry_ledger_clear_blank_entry (ledger);
393 gnc_entry_ledger_display_fini (ledger);
394 gnc_table_destroy (ledger->table);
395 qof_query_destroy (ledger->query);
396 g_free (ledger);
397}
398
399Table * gnc_entry_ledger_get_table (GncEntryLedger *ledger)
400{
401 if (!ledger) return NULL;
402 return ledger->table;
403}
404
405void gnc_entry_ledger_set_default_order (GncEntryLedger *ledger,
406 GncOrder *order)
407{
408 if (!ledger) return;
409 ledger->order = order;
410
411 if (!ledger->query && order)
412 {
413 ledger->query = qof_query_create_for (GNC_ENTRY_MODULE_NAME);
414 qof_query_set_book (ledger->query, gncOrderGetBook (order));
415 qof_query_add_guid_match (ledger->query,
416 g_slist_prepend (g_slist_prepend (NULL,
417 QOF_PARAM_GUID),
418 ENTRY_ORDER),
419 gncOrderGetGUID (order), QOF_QUERY_AND);
420 }
421 gnc_entry_ledger_display_refresh (ledger);
422}
423
424static void create_invoice_query (GncEntryLedger *ledger)
425{
426 QofQuery *q, *q1;
427 char * type = NULL;
428
429 if (!ledger->invoice)
430 return;
431
432 if (ledger->query)
433 qof_query_destroy (ledger->query);
434
435 /* Match: (where I-TYPE == Invoice or Bill)
436 *
437 * 1. book AND
438 * 2. ( Entry->I-TYPE == ledger->invoice
439 * #if I-TYPE == Invoice/Cust Credit Note (entry only)
440 * OR
441 * 3. ( Entry->Invoice == NULL AND
442 * ( Entry->Billable == TRUE AND
443 * Entry->Bill->Is-Posted? == TRUE AND
444 * ( Entry->BillTo == Invoice->parent OR
445 * ( Entry->BillTo == NULL AND Entry->Bill->BillTo == Invoice->parent ) ) )
446 * OR
447 * ( Entry->Order->real-parent == Invoice->parent ) )
448 * #endif
449 * )
450 *
451 * Note that term 3 is only for Editable invoices.
452 */
453
454 /* Term 1 */
455 ledger->query = qof_query_create_for (GNC_ENTRY_MODULE_NAME);
456 qof_query_set_book (ledger->query, gncInvoiceGetBook (ledger->invoice));
457
458 /* Term 2 */
459 switch (ledger->type)
460 {
461 case GNCENTRY_INVOICE_ENTRY:
462 case GNCENTRY_INVOICE_VIEWER:
463 case GNCENTRY_CUST_CREDIT_NOTE_ENTRY:
464 case GNCENTRY_CUST_CREDIT_NOTE_VIEWER:
465 type = ENTRY_INVOICE;
466 break;
467 case GNCENTRY_BILL_ENTRY:
468 case GNCENTRY_BILL_VIEWER:
469 case GNCENTRY_EXPVOUCHER_ENTRY:
470 case GNCENTRY_EXPVOUCHER_VIEWER:
471 case GNCENTRY_VEND_CREDIT_NOTE_ENTRY:
472 case GNCENTRY_VEND_CREDIT_NOTE_VIEWER:
473 case GNCENTRY_EMPL_CREDIT_NOTE_ENTRY:
474 case GNCENTRY_EMPL_CREDIT_NOTE_VIEWER:
475 type = ENTRY_BILL;
476 break;
477 default:
478 g_warning ("Invalid Ledger type");
479 type = ENTRY_INVOICE;
480 break;
481 }
482
483 q = qof_query_create_for (GNC_ENTRY_MODULE_NAME);
484 qof_query_add_guid_match (q, qof_query_build_param_list (type, QOF_PARAM_GUID, NULL),
485 gncInvoiceGetGUID (ledger->invoice), QOF_QUERY_OR);
486
487 /* Term 3 */
488 if ((ledger->type == GNCENTRY_INVOICE_ENTRY ||
489 ledger->type == GNCENTRY_CUST_CREDIT_NOTE_ENTRY) &&
490 gncOwnerGetEndGUID (gncInvoiceGetOwner (ledger->invoice)) != NULL)
491 {
492
493 const GncGUID *invoice_parent =
494 gncOwnerGetGUID (gncInvoiceGetOwner (ledger->invoice));
495 QofQuery *q2 = qof_query_create_for (GNC_ENTRY_MODULE_NAME);
496
497 /*
498 * Entry->BillTo == Invoice->parent OR
499 * ( Entry->BillTo == NULL AND Entry->Bill->BillTo == Invoice->parent )
500 */
501
502 qof_query_add_guid_match (q2, qof_query_build_param_list (ENTRY_BILLTO,
503 QOF_PARAM_GUID, NULL),
504 NULL, QOF_QUERY_AND);
505 qof_query_add_guid_match (q2, qof_query_build_param_list (ENTRY_BILL, INVOICE_BILLTO,
506 QOF_PARAM_GUID, NULL),
507 invoice_parent, QOF_QUERY_AND);
508 qof_query_add_guid_match (q2, qof_query_build_param_list (ENTRY_BILLTO,
509 QOF_PARAM_GUID, NULL),
510 invoice_parent, QOF_QUERY_OR);
511
512 /* Entry->Billable == TRUE AND Entry->Bill->Is-Posted? == TRUE */
513 qof_query_add_boolean_match (q2, qof_query_build_param_list (ENTRY_BILLABLE, NULL),
514 TRUE, QOF_QUERY_AND);
515 qof_query_add_boolean_match (q2, qof_query_build_param_list (ENTRY_BILL,
516 INVOICE_IS_POSTED, NULL),
517 TRUE, QOF_QUERY_AND);
518
519 /* Entry->Order->real-parent == Invoice->parent */
520 qof_query_add_guid_match (q2, qof_query_build_param_list (ENTRY_ORDER, ORDER_OWNER,
521 OWNER_PARENTG, NULL),
522 invoice_parent, QOF_QUERY_OR);
523
524 /* Entry->Invoice == NULL */
525 qof_query_add_guid_match (q2, qof_query_build_param_list (ENTRY_INVOICE,
526 QOF_PARAM_GUID, NULL),
527 NULL, QOF_QUERY_AND);
528
529
530 /* Combine terms 2 and 3 */
531 q1 = qof_query_merge (q, q2, QOF_QUERY_OR);
534 q = q1;
535 }
536
537 /* Combine terms 1 and 2 */
538 q1 = qof_query_merge (ledger->query, q, QOF_QUERY_AND);
540 qof_query_destroy (ledger->query);
541 ledger->query = q1;
542}
543
544void gnc_entry_ledger_set_default_invoice (GncEntryLedger *ledger,
545 GncInvoice *invoice)
546{
547 if (!ledger) return;
548 ledger->invoice = invoice;
549
550 /* For bills only, set the default date for new entries
551 * to the bill's opened date. This saves a lot of typing when
552 * adding bills on a day different from the bill's own date.
553 * Note this is for bills only, because for (customer's) invoices
554 * it makes more sense to use the current date.
555 * Consult https://bugs.gnucash.org/show_bug.cgi?id=646541
556 * to understand why.
557 */
558 if (gncInvoiceGetOwnerType (invoice) == GNC_OWNER_VENDOR)
559 ledger->last_date_entered = time64_to_gdate(gncInvoiceGetDateOpened (invoice));
560
561 if (!ledger->query && invoice)
562 create_invoice_query (ledger);
563
564 gnc_entry_ledger_display_refresh (ledger);
565}
566
567void gnc_entry_ledger_reset_query (GncEntryLedger *ledger)
568{
569 if (!ledger) return;
570 if (!ledger->invoice) return;
571
572 create_invoice_query (ledger);
573 gnc_entry_ledger_display_refresh (ledger);
574}
575
576void gnc_entry_ledger_set_parent (GncEntryLedger *ledger, GtkWidget *parent)
577{
578 if (!ledger) return;
579 ledger->parent = parent;
580}
581
582gboolean gnc_entry_ledger_find_entry (GncEntryLedger *ledger, GncEntry *entry,
583 VirtualCellLocation *vcell_loc)
584{
585 Table *table = ledger->table;
586 int v_row;
587 GncEntry *e;
588
589 for (v_row = 1; v_row < table->num_virt_rows; v_row++)
590 {
591 VirtualCellLocation vc_loc = { v_row, 0 };
592
593 e = gnc_entry_ledger_get_entry (ledger, vc_loc);
594
595 if (e == entry)
596 {
597 if (vcell_loc != NULL)
598 *vcell_loc = vc_loc;
599 return TRUE;
600 }
601 }
602 return FALSE;
603}
604
605void gnc_entry_ledger_set_readonly (GncEntryLedger *ledger, gboolean readonly)
606{
607 if (!ledger) return;
608 if (!readonly && qof_book_is_readonly(ledger->book)) return;
609
610 /* reset the ledger type appropriately */
611 if (readonly)
612 {
613 switch (ledger->type)
614 {
615 case GNCENTRY_ORDER_ENTRY:
616 ledger->type = GNCENTRY_ORDER_VIEWER;
617 break;
618 case GNCENTRY_INVOICE_ENTRY:
619 ledger->type = GNCENTRY_INVOICE_VIEWER;
620 create_invoice_query (ledger);
621 break;
622 case GNCENTRY_BILL_ENTRY:
623 ledger->type = GNCENTRY_BILL_VIEWER;
624 create_invoice_query (ledger);
625 break;
626 case GNCENTRY_EXPVOUCHER_ENTRY:
627 ledger->type = GNCENTRY_EXPVOUCHER_VIEWER;
628 create_invoice_query (ledger);
629 break;
630 case GNCENTRY_CUST_CREDIT_NOTE_ENTRY:
631 ledger->type = GNCENTRY_CUST_CREDIT_NOTE_VIEWER;
632 create_invoice_query (ledger);
633 break;
634 case GNCENTRY_VEND_CREDIT_NOTE_ENTRY:
635 ledger->type = GNCENTRY_VEND_CREDIT_NOTE_VIEWER;
636 create_invoice_query (ledger);
637 break;
638 case GNCENTRY_EMPL_CREDIT_NOTE_ENTRY:
639 ledger->type = GNCENTRY_EMPL_CREDIT_NOTE_VIEWER;
640 create_invoice_query (ledger);
641 break;
642 default:
643 return; /* Nothing to do */
644 }
645 }
646 else
647 {
648 switch (ledger->type)
649 {
650 case GNCENTRY_ORDER_VIEWER:
651 ledger->type = GNCENTRY_ORDER_ENTRY;
652 break;
653 case GNCENTRY_INVOICE_VIEWER:
654 ledger->type = GNCENTRY_INVOICE_ENTRY;
655 create_invoice_query (ledger);
656 break;
657 case GNCENTRY_BILL_VIEWER:
658 ledger->type = GNCENTRY_BILL_ENTRY;
659 create_invoice_query (ledger);
660 break;
661 case GNCENTRY_EXPVOUCHER_VIEWER:
662 ledger->type = GNCENTRY_EXPVOUCHER_ENTRY;
663 create_invoice_query (ledger);
664 break;
665 case GNCENTRY_CUST_CREDIT_NOTE_VIEWER:
666 ledger->type = GNCENTRY_CUST_CREDIT_NOTE_ENTRY;
667 create_invoice_query (ledger);
668 break;
669 case GNCENTRY_VEND_CREDIT_NOTE_VIEWER:
670 ledger->type = GNCENTRY_VEND_CREDIT_NOTE_ENTRY;
671 create_invoice_query (ledger);
672 break;
673 case GNCENTRY_EMPL_CREDIT_NOTE_VIEWER:
674 ledger->type = GNCENTRY_EMPL_CREDIT_NOTE_ENTRY;
675 create_invoice_query (ledger);
676 break;
677 default:
678 return; /* Nothing to do */
679 }
680 }
681
682 /* reset the model */
683 gnc_table_model_set_read_only (ledger->table->model, readonly);
684
685 /* if readonly is TRUE, get rid of the blank entry. */
686 if (readonly)
687 gnc_entry_ledger_clear_blank_entry (ledger);
688
689 /* and refresh the display */
690 gnc_entry_ledger_display_refresh (ledger);
691}
692
693gboolean
694gnc_entry_ledger_changed (GncEntryLedger *ledger)
695{
696 if (!ledger)
697 return FALSE;
698
699 if (gnc_table_current_cursor_changed (ledger->table, FALSE))
700 return TRUE;
701
702 return FALSE;
703}
704
705void
706gnc_entry_ledger_compute_value (GncEntryLedger *ledger,
707 gnc_numeric *value, gnc_numeric *tax_value)
708{
709 gnc_numeric qty = gnc_numeric_zero(), price = gnc_numeric_zero(), discount = gnc_numeric_zero();
710 gint disc_type, disc_how;
711 gboolean taxable, taxincluded;
713 GList *taxes = NULL;
714 int denom = 100;
715 gnc_numeric value_unrounded, taxes_unrounded;
716 GncEntry *entry;
717
718 gnc_entry_ledger_get_numeric (ledger, ENTRY_QTY_CELL, &qty);
719 entry = gnc_entry_ledger_get_current_entry (ledger);
720 if (ledger->is_cust_doc)
721 price = gncEntryGetInvPrice (entry);
722 else
723 price = gncEntryGetBillPrice (entry);
724 gnc_entry_ledger_get_numeric (ledger, ENTRY_DISC_CELL, &discount);
725
726 disc_type = gnc_entry_ledger_get_type (ledger, ENTRY_DISTYPE_CELL);
727 disc_how = gnc_entry_ledger_get_type (ledger, ENTRY_DISHOW_CELL);
728
729 /* Some ledger types (see full list in gnc_entry_ledger_set_cells)
730 don't have discounts */
731 if (ledger->type == GNCENTRY_BILL_ENTRY ||
732 ledger->type == GNCENTRY_BILL_VIEWER ||
733 ledger->type == GNCENTRY_VEND_CREDIT_NOTE_ENTRY ||
734 ledger->type == GNCENTRY_VEND_CREDIT_NOTE_VIEWER ||
735 ledger->type == GNCENTRY_EXPVOUCHER_ENTRY ||
736 ledger->type == GNCENTRY_EXPVOUCHER_VIEWER ||
737 ledger->type == GNCENTRY_EMPL_CREDIT_NOTE_ENTRY ||
738 ledger->type == GNCENTRY_EMPL_CREDIT_NOTE_VIEWER)
739 {
740 g_assert (gnc_numeric_zero_p (discount));
741 disc_type = GNC_AMT_TYPE_VALUE;
742 disc_how = GNC_DISC_PRETAX;
743 }
744
745
746 /* If we're so early in the process that we don't have info, stop now */
747 if (disc_type < 0 || disc_how < 0)
748 {
749 if (value)
750 *value = gnc_numeric_zero ();
751 if (tax_value)
752 *tax_value = gnc_numeric_zero ();
753 return;
754 }
755
756 taxable = gnc_entry_ledger_get_checkmark (ledger, ENTRY_TAXABLE_CELL);
757 taxincluded = gnc_entry_ledger_get_checkmark (ledger, ENTRY_TAXINCLUDED_CELL);
758 table = gnc_entry_ledger_get_taxtable (ledger, ENTRY_TAXTABLE_CELL);
759
760 /* Expense vouchers don't have taxable, taxincluded, or taxtable cells, either */
761 if (ledger->type == GNCENTRY_EXPVOUCHER_ENTRY ||
762 ledger->type == GNCENTRY_EXPVOUCHER_VIEWER)
763 {
764 taxable = FALSE;
765 taxincluded = FALSE;
766 table = NULL;
767 }
768
769 if (ledger->invoice)
770 {
771 gnc_commodity *currency = gncInvoiceGetCurrency(ledger->invoice);
772 if (currency)
773 denom = gnc_commodity_get_fraction(currency);
774 }
775
776 gncEntryComputeValue (qty, price, (taxable ? table : NULL), taxincluded,
777 discount, disc_type, disc_how, 0,
778 &value_unrounded, NULL, &taxes);
779
780 if (value)
781 *value = gnc_numeric_convert (value_unrounded, denom,
782 GNC_HOW_RND_ROUND_HALF_UP);
783
784 /* return the tax value */
785 taxes_unrounded = gncAccountValueTotal (taxes);
787 if (tax_value)
788 *tax_value = gnc_numeric_convert (taxes_unrounded, denom,
789 GNC_HOW_RND_ROUND_HALF_UP);
790}
791
792gboolean
793gnc_entry_ledger_get_entry_virt_loc (GncEntryLedger *ledger, const GncEntry *entry,
794 VirtualCellLocation *vcell_loc)
795{
796 Table *table;
797 int v_row;
798 int v_col;
799
800 if ((ledger == NULL) || (entry == NULL))
801 return FALSE;
802 g_assert(vcell_loc);
803
804 table = ledger->table;
805
806 /* go backwards because typically you search for entries at the end */
807
808 for (v_row = table->num_virt_rows - 1; v_row > 0; v_row--)
809 for (v_col = 0; v_col < table->num_virt_cols; v_col++)
810 {
811 VirtualCellLocation vc_loc = { v_row, v_col };
812 VirtualCell *vcell;
813 GncEntry *e;
814
815 vcell = gnc_table_get_virtual_cell (table, vc_loc);
816 if (vcell == NULL)
817 continue;
818
819 if (!vcell->visible)
820 continue;
821
822 e = gncEntryLookup (ledger->book, vcell->vcell_data);
823
824 if (e == entry)
825 {
826 if (vcell_loc)
827 *vcell_loc = vc_loc;
828
829 return TRUE;
830 }
831 }
832
833 return FALSE;
834}
835
836void
837gnc_entry_ledger_delete_current_entry (GncEntryLedger *ledger)
838{
839 GncEntry *entry;
840
841 if (!ledger)
842 return;
843
844 /* If there is no entry, just return */
845 entry = gnc_entry_ledger_get_current_entry (ledger);
846 if (!entry)
847 return;
848
849 /* If this is the blank entry, just cancel the changes */
850 if (entry == gnc_entry_ledger_get_blank_entry (ledger))
851 {
852 gnc_entry_ledger_cancel_cursor_changes (ledger);
853 return;
854 }
855
856 /* Ok, let's delete this entry */
857 gnc_suspend_gui_refresh ();
858 if (!gncEntryIsOpen (entry))
859 gncEntryBeginEdit (entry);
860
861 {
862 GncOrder *order;
863 GncInvoice *invoice;
864
865 order = gncEntryGetOrder (entry);
866 if (order)
867 gncOrderRemoveEntry (order, entry);
868
869 invoice = gncEntryGetInvoice (entry);
870 if (invoice)
871 gncInvoiceRemoveEntry (invoice, entry);
872
873 invoice = gncEntryGetBill (entry);
874 if (invoice)
875 gncBillRemoveEntry (invoice, entry);
876
877 gncEntryDestroy (entry);
878 }
879 gnc_resume_gui_refresh ();
880}
881
882void
883gnc_entry_ledger_duplicate_current_entry (GncEntryLedger *ledger)
884{
885 GncEntry *entry;
886 gboolean changed;
887
888 if (!ledger)
889 return;
890
891 /* Be paranoid */
892 entry = gnc_entry_ledger_get_current_entry (ledger);
893 if (!entry)
894 return;
895
896 changed = gnc_table_current_cursor_changed (ledger->table, FALSE);
897
898 /* See if we're asked to duplicate an unchanged blank entry --
899 * there is no point in doing that.
900 */
901 if (!changed && entry == gnc_entry_ledger_get_blank_entry (ledger))
902 return;
903
904 gnc_suspend_gui_refresh ();
905
906 /* If the cursor has been edited, we are going to have to commit
907 * it before we can duplicate. Make sure the user wants to do that. */
908 if (changed)
909 {
910 const char *title = _("Save the current entry?");
911 const char *message =
912 _("The current transaction has been changed. Would you like to "
913 "record the changes before duplicating this entry, or "
914 "cancel the duplication?");
915 GtkWidget *dialog;
916 gint response;
917
918 dialog = gtk_message_dialog_new(GTK_WINDOW(ledger->parent),
919 GTK_DIALOG_DESTROY_WITH_PARENT,
920 GTK_MESSAGE_QUESTION,
921 GTK_BUTTONS_NONE,
922 "%s", title);
923 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
924 "%s", message);
925 gtk_dialog_add_buttons(GTK_DIALOG(dialog),
926 _("_Cancel"), GTK_RESPONSE_CANCEL,
927 _("_Record"), GTK_RESPONSE_ACCEPT,
928 NULL);
929 response = gnc_dialog_run(GTK_DIALOG(dialog), GNC_PREF_WARN_INV_ENTRY_DUP);
930 gtk_widget_destroy(dialog);
931
932 if (response != GTK_RESPONSE_ACCEPT)
933 {
934 gnc_resume_gui_refresh ();
935 return;
936 }
937
938 if (!gnc_entry_ledger_commit_entry (ledger))
939 {
940 gnc_resume_gui_refresh ();
941 return;
942 }
943 }
944
945 /* Ok, we're ready to make the copy */
946 {
947 GncEntry * new_entry;
948
949 new_entry = gncEntryCreate (ledger->book);
950 gncEntryCopy (entry, new_entry, TRUE);
951 gncEntrySetDateGDate (new_entry, &ledger->last_date_entered);
952
953 /* We also must set a new DateEntered on the new entry
954 * because otherwise the ordering is not deterministic */
955 gncEntrySetDateEntered (new_entry, gnc_time (NULL));
956
957 /* Set the hint for where to display on the refresh */
958 ledger->hint_entry = new_entry;
959 }
960
961 gnc_resume_gui_refresh ();
962 return;
963}
964
965QofQuery *
966gnc_entry_ledger_get_query (GncEntryLedger *ledger)
967{
968 if (!ledger)
969 return NULL;
970
971 return ledger->query;
972}
973
974void
975gnc_entry_ledger_set_prefs_group (GncEntryLedger *ledger, const gchar *string)
976{
977 if (!ledger)
978 return;
979
980 ledger->prefs_group = string;
981}
982
984 gboolean move_up)
985{
986 GncEntry *blank, *current, *target;
987 VirtualCellLocation vcell_loc;
988
989 g_assert(ledger);
990
991 blank = gnc_entry_ledger_get_blank_entry(ledger);
992 if (!blank)
993 return;
994
995 /* Ensure we have a valid current GncEntry and it isn't the blank
996 * entry */
997 current = gnc_entry_ledger_get_current_entry(ledger);
998 if (!current || current == blank)
999 return;
1000
1001 /* Obtain the GncEntry at the up or down virtual table location */
1002 vcell_loc = ledger->table->current_cursor_loc.vcell_loc;
1003 if (move_up)
1004 {
1005 if (vcell_loc.virt_row == 0)
1006 return;
1007 vcell_loc.virt_row--;
1008 }
1009 else
1010 {
1011 vcell_loc.virt_row++;
1012 }
1013
1014 /* Ensure we have a valid other GncEntry and it isn't the blank
1015 * entry */
1016 target = gnc_entry_ledger_get_entry(ledger, vcell_loc);
1017 if (!target || target == blank)
1018 return;
1019
1020 /* Also, only continue if both have the same date, because the
1021 * "standard ordering" is tied to the date anyway. Note: This
1022 * unfortunately prevents the user from changing the ordering if
1023 * he has chosen a different sort order and the sort key happens
1024 * to be equal among the two entries. But I don't know how to look
1025 * up the current sort ordering from here, so I cowardly refuse to
1026 * tweak the EntryDate in this case. */
1027 {
1028 time64 t1, t2;
1029 GDate d1 = gncEntryGetDateGDate(current),
1030 d2 = gncEntryGetDateGDate(target);
1031 if (g_date_compare(&d1, &d2) != 0)
1032 return;
1033
1034 /* Special treatment if the equality doesn't hold if we access the
1035 dates as time64. See the comment in gncEntrySetDateGDate() for the
1036 reason: Some code used the time64 at noon for the EntryDate, other
1037 code used the time64 at the start of day. */
1038 t1 = gncEntryGetDate(current);
1039 t2 = gncEntryGetDate(target);
1040 if (t1 != t2)
1041 {
1042 /* times are not equal, even though the GDates were equal? Then
1043 we set the GDates again. This will force the times to be equal
1044 as well. */
1045 gncEntrySetDateGDate(current, &d1);
1046 gncEntrySetDateGDate(target, &d2);
1047 }
1048 }
1049
1050 /*g_warning("Ok, current desc='%s' target desc='%s'",
1051 gncEntryGetDescription(current),
1052 gncEntryGetDescription(target));*/
1053
1054 gnc_suspend_gui_refresh ();
1055
1056 /* Swap the date-entered of both entries. That's already
1057 * sufficient! */
1058 {
1059 time64 time_current = gncEntryGetDateEntered(current);
1060 time64 time_target = gncEntryGetDateEntered(target);
1061
1062 /* Special treatment for identical times (potentially caused
1063 * by the "duplicate entry" command) */
1064 if (time_current == time_target)
1065 {
1066 /*g_warning("Surprise - both DateEntered are equal.");*/
1067 /* We just increment the DateEntered of the previously
1068 * lower of the two by one second. This might still cause
1069 * issues if multiple entries had this problem, but
1070 * whatever. */
1071 if (move_up)
1072 ++time_current;
1073 else
1074 ++time_target;
1075 }
1076
1077 /* Write the new DateEntered. */
1078 gncEntrySetDateEntered(current, time_target);
1079 gncEntrySetDateEntered(target, time_current);
1080
1081 /* And finally let the GncInvoice sort its entries
1082 * accordingly, so that the invoice reports will give the same
1083 * ordering as the register window. */
1084 gncInvoiceSortEntries(ledger->invoice);
1085 }
1086
1087 gnc_resume_gui_refresh ();
1088}
Account handling public routines.
This file contains the functions to present a gui to the user for creating a new account or editing a...
utility functions for the GnuCash UI
Business Entry Interface.
gboolean qof_book_is_readonly(const QofBook *book)
Return whether the book is read only.
Definition qofbook.cpp:497
TableModel * gnc_entry_ledger_model_new(GncEntryLedgerType type)
Public Interface.
GncEntry * gnc_entry_ledger_get_current_entry(GncEntryLedger *ledger)
Returns the Entry where the cursor is currently located.
GncEntry * gnc_entry_ledger_get_entry(GncEntryLedger *ledger, VirtualCellLocation vcell_loc)
Returns the GncEntry at the given location, or NULL if the location is not valid.
GncEntryLedger * gnc_entry_ledger_new(QofBook *book, GncEntryLedgerType type)
Create and return a new GncEntry Ledger.
void gnc_entry_ledger_destroy(GncEntryLedger *ledger)
Destroy the GncEntry Ledger.
gboolean gnc_entry_ledger_commit_entry(GncEntryLedger *ledger)
This will act just like hitting 'return' to record an entry.
Table * gnc_entry_ledger_get_table(GncEntryLedger *ledger)
Get the Table.
GncEntry * gnc_entry_ledger_get_blank_entry(GncEntryLedger *ledger)
Exported Functions.
gboolean gnc_entry_ledger_get_entry_virt_loc(GncEntryLedger *ledger, const GncEntry *entry, VirtualCellLocation *vcell_loc)
Looks up the cell location of the given "entry" and writes the location into the variable pointed to ...
void gnc_entry_ledger_set_default_invoice(GncEntryLedger *ledger, GncInvoice *invoice)
Set the default invoice for this ledger.
void gnc_entry_ledger_set_default_order(GncEntryLedger *ledger, GncOrder *order)
Set the default order for this ledger.
void gnc_entry_ledger_move_current_entry_updown(GncEntryLedger *ledger, gboolean move_up)
This implements the command of moving the current entry (where the cursor is currently located) one r...
void gnc_combo_cell_set_autosize(ComboCell *cell, gboolean autosize)
Determines whether the popup list autosizes itself or uses all available space.
void gnc_combo_cell_set_strict(ComboCell *cell, gboolean strict)
Determines whether the cell will accept strings not in the menu.
gnc_numeric gnc_price_cell_get_value(PriceCell *cell)
return the value of a price cell
Definition pricecell.c:208
void gnc_combo_cell_add_menu_item(ComboCell *cell, const char *menustr)
Add a menu item to the list.
int gnc_commodity_get_fraction(const gnc_commodity *cm)
Retrieve the fraction for the specified commodity.
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
Definition gnc-date.h:87
GDate * gnc_g_date_new_today()
Returns a newly allocated date of the current clock time, taken from time(2).
GDate time64_to_gdate(time64 t)
Returns the GDate in which the time64 occurs.
time64 gnc_time(time64 *tbuf)
get the current time
Definition gnc-date.cpp:262
gboolean xaccAccountGetPlaceholder(const Account *acc)
Get the "placeholder" flag for an account.
Definition Account.cpp:4119
Account * gnc_account_lookup_by_code(const Account *parent, const char *code)
The gnc_account_lookup_by_code() subroutine works like gnc_account_lookup_by_name,...
Definition Account.cpp:3106
GDate gncEntryGetDateGDate(const GncEntry *entry)
Returns the day of this entry.
Definition gncEntry.c:918
time64 gncEntryGetDate(const GncEntry *entry)
DEPRECATED - use gncEntryGetDateGDate() instead! (Because the time-of-day is a misleading extra infor...
Definition gncEntry.c:913
void gncEntrySetDateGDate(GncEntry *entry, const GDate *date)
Set the date of this entry.
Definition gncEntry.c:504
const GncGUID * guid_null(void)
Returns a GncGUID which is guaranteed to never reference any entity.
Definition guid.cpp:165
Account * gnc_ui_new_accounts_from_name_with_defaults(GtkWindow *parent, const char *name, GList *valid_types, const gnc_commodity *default_commodity, Account *parent_acct)
Display a modal window for creating a new account.
char * gnc_get_account_name_for_register(const Account *account)
Get either the full name of the account or the simple name, depending on the configuration parameter ...
Account * gnc_account_lookup_for_register(const Account *base_account, const char *name)
Retrieve the account matching the given name starting from the descendants of base_account.
void gncInvoiceSortEntries(GncInvoice *invoice)
Call this function when an Entry is changed and you want to re-sort the list of entries.
Definition gncInvoice.c:750
#define gncInvoiceGetGUID(x)
deprecated functions
Definition gncInvoice.h:314
#define PWARN(format, args...)
Log a warning.
Definition qoflog.h:250
gboolean gnc_numeric_zero_p(gnc_numeric a)
Returns 1 if the given gnc_numeric is 0 (zero), else returns 0.
const GncGUID * gncOwnerGetGUID(const GncOwner *owner)
Get the GncGUID of the immediate owner.
Definition gncOwner.c:518
void qof_query_add_guid_match(QofQuery *q, QofQueryParamList *param_list, const GncGUID *guid, QofQueryOp op)
DOCUMENT ME !!
QofQuery * qof_query_merge(QofQuery *q1, QofQuery *q2, QofQueryOp op)
Combine two queries together using the Boolean set (logical) operator 'op'.
void qof_query_set_book(QofQuery *query, QofBook *book)
Set the book to be searched.
void qof_query_add_boolean_match(QofQuery *q, QofQueryParamList *param_list, gboolean value, QofQueryOp op)
Handy-dandy convenience routines, avoids having to create a separate predicate for boolean matches.
void qof_query_destroy(QofQuery *query)
Frees the resources associate with a Query object.
void gnc_table_move_cursor(Table *table, VirtualLocation new_virt_loc)
will move the cursor (but not the cursor GUI) to the indicated location.
gboolean gnc_table_find_close_valid_cell(Table *table, VirtualLocation *virt_loc, gboolean exact_pointer)
Find a close valid cell.
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,...
#define CURSOR_HEADER
Standard Cursor Names.
VirtualCell * gnc_table_get_virtual_cell(Table *table, VirtualCellLocation vcell_loc)
returns the virtual cell associated with a particular virtual location.
void gnc_table_set_vcell(Table *table, CellBlock *cursor, gconstpointer vcell_data, gboolean visible, gboolean start_primary_color, VirtualCellLocation vcell_loc)
Indicate what handler should be used for a given virtual block.
void gncAccountValueDestroy(GList *list)
Destroy a list of accountvalues.
gnc_numeric gncAccountValueTotal(GList *list)
return the total for this list
@ GNC_AMT_TYPE_VALUE
tax is a number
Definition gncTaxTable.h:80
STRUCTS.
The CheckboxCell object implements a cell handler that will toggle between yes and no values when cli...
The ComboCell object implements a cell handler with a "combination-box" pull-down menu in it.
Definition combocell.h:53
The type used to store guids in C.
Definition guid.h:75
modtime is the internal date of the last modtime See libgnucash/engine/TaxTableBillTermImmutability....
The PriceCell object implements a cell handler that stores a single double-precision value,...
Definition pricecell.h:55
QofBook reference.
Definition qofbook-p.hpp:47
The RecnCell object implements a cell handler that will cycle through a series of single-character va...
Definition recncell.h:48
holds information about each virtual cell.
gpointer vcell_data
Array of physical cells.
unsigned int visible
Used by higher-level code.
A Query.
Definition qofquery.cpp:75