GnuCash c935c2f+
Loading...
Searching...
No Matches
gncEntryLedgerControl.c
Go to the documentation of this file.
1
7/*
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of
11 * the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, contact:
20 *
21 * Free Software Foundation Voice: +1-617-542-5942
22 * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
23 * Boston, MA 02110-1301, USA gnu@gnu.org
24 */
25
26#include <config.h>
27
28#include <glib.h>
29#include <glib/gi18n.h>
30
31#include "Account.h"
32#include "combocell.h"
33#include "dialog-account.h"
34#include "dialog-utils.h"
35#include "gnc-component-manager.h"
36#include "gnc-prefs.h"
37#include "gnc-ui.h"
38#include "gnc-ui-util.h"
39#include "gnc-gui-query.h"
40#include "gnc-warnings.h"
41#include "table-allgui.h"
42#include "pricecell.h"
43#include "dialog-tax-table.h"
44#include "checkboxcell.h"
45
46#include "gncEntryLedgerP.h"
48
49
50static gboolean
51gnc_entry_ledger_save (GncEntryLedger *ledger, gboolean do_commit)
52{
53 GncEntry *blank_entry;
54 GncEntry *entry;
55
56 if (!ledger) return FALSE;
57
58 blank_entry = gnc_entry_ledger_get_blank_entry (ledger);
59
61 if (entry == NULL) return FALSE;
62
63 /* Try to avoid heavy-weight updates if nothing has changed */
64 if (!gnc_table_current_cursor_changed (ledger->table, FALSE))
65 {
66 if (!do_commit) return FALSE;
67
68 if (entry == blank_entry)
69 {
70 if (ledger->blank_entry_edited)
71 {
72 ledger->last_date_entered = gncEntryGetDateGDate (entry);
73 ledger->blank_entry_guid = *guid_null ();
74 ledger->blank_entry_edited = FALSE;
75 blank_entry = NULL;
76 }
77 else
78 return FALSE;
79 }
80
81 return TRUE;
82 }
83
84 gnc_suspend_gui_refresh ();
85
86 if (!gncEntryIsOpen (entry))
87 gncEntryBeginEdit (entry);
88
89 gnc_table_save_cells (ledger->table, entry);
90
91 if (entry == blank_entry)
92 {
93 time64 time = gnc_time (NULL);
94 gncEntrySetDateEntered (blank_entry, time);
95
96 switch (ledger->type)
97 {
98 case GNCENTRY_ORDER_ENTRY:
99 gncOrderAddEntry (ledger->order, blank_entry);
100 break;
101 case GNCENTRY_INVOICE_ENTRY:
102 case GNCENTRY_CUST_CREDIT_NOTE_ENTRY:
103 /* Anything entered on an invoice entry must be part of the invoice! */
104 gncInvoiceAddEntry (ledger->invoice, blank_entry);
105 break;
106 case GNCENTRY_BILL_ENTRY:
107 case GNCENTRY_EXPVOUCHER_ENTRY:
108 case GNCENTRY_VEND_CREDIT_NOTE_ENTRY:
109 case GNCENTRY_EMPL_CREDIT_NOTE_ENTRY:
110 /* Anything entered on an invoice entry must be part of the invoice! */
111 gncBillAddEntry (ledger->invoice, blank_entry);
112 break;
113 default:
114 /* Nothing to do for viewers */
115 g_warning ("blank entry traversed in a viewer");
116 break;
117 }
118 }
119
120 if (entry == blank_entry)
121 {
122 if (do_commit)
123 {
124 ledger->blank_entry_guid = *guid_null ();
125 blank_entry = NULL;
126 ledger->last_date_entered = gncEntryGetDateGDate (entry);
127 }
128 else
129 ledger->blank_entry_edited = TRUE;
130 }
131
132 if (do_commit)
133 gncEntryCommitEdit (entry);
134
135 gnc_table_clear_current_cursor_changes (ledger->table);
136
137 gnc_resume_gui_refresh ();
138
139 return TRUE;
140}
141
142static gboolean
143gnc_entry_ledger_verify_acc_cell_ok (GncEntryLedger *ledger,
144 const char *cell_name,
145 const char *cell_msg)
146{
147 ComboCell *cell;
148 const char *name;
149
150 cell = (ComboCell *) gnc_table_layout_get_cell (ledger->table->layout,
151 cell_name);
152 g_return_val_if_fail (cell, TRUE);
153 name = cell->cell.value;
154 if (!name || *name == '\0')
155 {
156 const char *format = ("%s %s");
157 const char *gen_msg = _("Invalid Entry: You need to supply an account in the right currency for this position.");
158
159 gnc_error_dialog (GTK_WINDOW (ledger->parent), format, gen_msg, cell_msg);
160 return FALSE;
161 }
162 return TRUE;
163}
164
168static gboolean
169gnc_entry_ledger_verify_can_save (GncEntryLedger *ledger)
170{
171 gnc_numeric value;
172
173 /* Compute the value and tax value of the current cursor */
174 gnc_entry_ledger_compute_value (ledger, &value, NULL);
175
176 /* If there is a value, make sure there is an account */
177 if (! gnc_numeric_zero_p (value))
178 {
179 switch (ledger->type)
180 {
181 case GNCENTRY_INVOICE_ENTRY:
182 case GNCENTRY_CUST_CREDIT_NOTE_ENTRY:
183 if (!gnc_entry_ledger_verify_acc_cell_ok (ledger, ENTRY_IACCT_CELL,
184 _("This account should usually be of type income.")))
185 return FALSE;
186 break;
187 case GNCENTRY_BILL_ENTRY:
188 case GNCENTRY_EXPVOUCHER_ENTRY:
189 case GNCENTRY_VEND_CREDIT_NOTE_ENTRY:
190 case GNCENTRY_EMPL_CREDIT_NOTE_ENTRY:
191 if (!gnc_entry_ledger_verify_acc_cell_ok (ledger, ENTRY_BACCT_CELL,
192 _("This account should usually be of type expense or asset.")))
193 return FALSE;
194 break;
195 default:
196 g_warning ("Unhandled ledger type");
197 break;
198 }
199 }
200
201 return TRUE;
202}
203
204static void gnc_entry_ledger_move_cursor (VirtualLocation *p_new_virt_loc,
205 gpointer user_data)
206{
207 GncEntryLedger *ledger = user_data;
208 VirtualLocation new_virt_loc = *p_new_virt_loc;
209 GncEntry *new_entry;
210 GncEntry *old_entry;
211 gboolean saved;
212
213 if (!ledger) return;
214
215 old_entry = gnc_entry_ledger_get_current_entry (ledger);
216 new_entry = gnc_entry_ledger_get_entry (ledger, new_virt_loc.vcell_loc);
217
218 gnc_suspend_gui_refresh ();
219 saved = gnc_entry_ledger_save (ledger, old_entry != new_entry);
220 gnc_resume_gui_refresh ();
221
222 /* redrawing can muck everything up */
223 if (saved)
224 {
225 VirtualCellLocation vcell_loc;
226
227 /* redraw */
228 gnc_entry_ledger_display_refresh (ledger);
229
230 if (ledger->traverse_to_new)
231 new_entry = gnc_entry_ledger_get_blank_entry (ledger);
232
233 /* if the entry we were going to is still in the register,
234 * then it may have moved. Find out where it is now. */
235 if (gnc_entry_ledger_find_entry (ledger, new_entry, &vcell_loc))
236 {
237 new_virt_loc.vcell_loc = vcell_loc;
238 }
239 else
240 new_virt_loc.vcell_loc = ledger->table->current_cursor_loc.vcell_loc;
241 }
242
243 gnc_table_find_close_valid_cell (ledger->table, &new_virt_loc, FALSE);
244
245 *p_new_virt_loc = new_virt_loc;
246}
247
252static QofQuery *new_query_for_entry_desc(GncEntryLedger *reg, const char* desc, gboolean use_invoice)
253{
254 QofQuery *query = NULL;
255 QofQueryPredData *predData = NULL;
256 GSList *param_list = NULL;
257 GSList *primary_sort_params = NULL;
258 const char* should_be_null = (use_invoice ? ENTRY_BILL : ENTRY_INVOICE);
259
260 g_assert(reg);
261 g_assert(desc);
262
263 /* The query itself and its book */
264 query = qof_query_create_for (GNC_ID_ENTRY);
265 qof_query_set_book (query, reg->book);
266
267 /* Predicate data: We want to compare one string, namely the given
268 * argument */
269 predData =
270 qof_query_string_predicate (QOF_COMPARE_EQUAL, desc,
271 QOF_STRING_MATCH_CASEINSENSITIVE, FALSE);
272
273 /* Search Parameter: We want to query on the ENTRY_DESC column */
274 param_list = qof_query_build_param_list (ENTRY_DESC, NULL);
275
276 /* Register this in the query */
277 qof_query_add_term (query, param_list, predData, QOF_QUERY_FIRST_TERM);
278
279 /* For invoice entries, Entry->Bill must be NULL, and vice versa */
281 qof_query_build_param_list (should_be_null,
282 QOF_PARAM_GUID, NULL),
283 NULL, QOF_QUERY_AND);
284
285 /* Set the sort order: By DATE_ENTERED, increasing, and returning
286 * only one single resulting item. */
287 primary_sort_params = qof_query_build_param_list(ENTRY_DATE_ENTERED, NULL);
288 qof_query_set_sort_order (query, primary_sort_params, NULL, NULL);
289 qof_query_set_sort_increasing (query, TRUE, TRUE, TRUE);
290
292
293 return query;
294}
295
298static GncEntry*
299find_entry_in_book_by_desc(GncEntryLedger *reg, const char* desc)
300{
301 GncEntry *result = NULL;
302 gboolean use_invoice;
303 QofQuery *query;
304 GList *entries = NULL;
305
306 switch (reg->type)
307 {
308 case GNCENTRY_INVOICE_ENTRY:
309 case GNCENTRY_INVOICE_VIEWER:
310 case GNCENTRY_CUST_CREDIT_NOTE_ENTRY:
311 case GNCENTRY_CUST_CREDIT_NOTE_VIEWER:
312 use_invoice = TRUE;
313 break;
314 default:
315 use_invoice = FALSE;
316 break;
317 };
318
319 query = new_query_for_entry_desc(reg, desc, use_invoice);
320 entries = qof_query_run(query);
321
322 /* Do we have a non-empty result? */
323 if (entries)
324 {
325 /* That's the result. */
326 result = (GncEntry*) entries->data;
327 /*g_warning("Found %d GncEntry items", g_list_length (entries));*/
328 }
329
330 qof_query_destroy(query);
331 return result;
332}
333
334#if 0
338static GncEntry*
339gnc_find_entry_in_reg_by_desc(GncEntryLedger *reg, const char* desc)
340{
341 int virt_row, virt_col;
342 int num_rows, num_cols;
343 GncEntry *last_entry;
344
345 g_assert(reg);
346 g_assert(reg->table);
347 if (!reg || !reg->table)
348 return NULL;
349
350 num_rows = reg->table->num_virt_rows;
351 num_cols = reg->table->num_virt_cols;
352
353 last_entry = NULL;
354
355 for (virt_row = num_rows - 1; virt_row >= 0; virt_row--)
356 for (virt_col = num_cols - 1; virt_col >= 0; virt_col--)
357 {
358 GncEntry *entry;
359 VirtualCellLocation vcell_loc = { virt_row, virt_col };
360
361 entry = gnc_entry_ledger_get_entry(reg, vcell_loc);
362
363 if (entry == last_entry)
364 continue;
365
366 if (g_strcmp0 (desc, gncEntryGetDescription (entry)) == 0)
367 return entry;
368
369 last_entry = entry;
370 }
371
372 return NULL;
373}
374#endif
375
376static void set_value_combo_cell(BasicCell *cell, const char *new_value)
377{
378 if (!cell || !new_value)
379 return;
380 if (g_strcmp0 (new_value, gnc_basic_cell_get_value (cell)) == 0)
381 return;
382
383 gnc_combo_cell_set_value ((ComboCell *) cell, new_value);
384 gnc_basic_cell_set_changed (cell, TRUE);
385}
386
387static void set_value_price_cell(BasicCell *cell, gnc_numeric new_value)
388{
389 PriceCell *pcell = (PriceCell*) cell;
390 if (!cell)
391 return;
392 if (gnc_numeric_equal (new_value, gnc_price_cell_get_value(pcell)))
393 return;
394
395 gnc_price_cell_set_value (pcell, new_value);
396 gnc_basic_cell_set_changed (cell, TRUE);
397}
398
399static gboolean
400gnc_entry_ledger_auto_completion (GncEntryLedger *ledger,
401 gncTableTraversalDir dir,
402 VirtualLocation *p_new_virt_loc)
403{
404 GncEntry *entry;
405 GncEntry *blank_entry;
406 GncEntry *auto_entry;
407 const char* cell_name;
408 const char *desc;
409 BasicCell *cell = NULL;
410 char *account_name = NULL;
411
412 g_assert(ledger);
413 g_assert(ledger->table);
414 blank_entry = gnc_entry_ledger_get_blank_entry (ledger);
415
416 /* auto-completion is only triggered by a tab out */
417 if (dir != GNC_TABLE_TRAVERSE_RIGHT)
418 return FALSE;
419
420 entry = gnc_entry_ledger_get_current_entry (ledger);
421 if (entry == NULL)
422 return FALSE;
423
424 cell_name = gnc_table_get_current_cell_name (ledger->table);
425
426 /* Auto-completion is done only in an entry ledger */
427 switch (ledger->type)
428 {
429 case GNCENTRY_ORDER_ENTRY:
430 case GNCENTRY_INVOICE_ENTRY:
431 case GNCENTRY_BILL_ENTRY:
432 case GNCENTRY_EXPVOUCHER_ENTRY:
433 case GNCENTRY_CUST_CREDIT_NOTE_ENTRY:
434 case GNCENTRY_VEND_CREDIT_NOTE_ENTRY:
435 case GNCENTRY_EMPL_CREDIT_NOTE_ENTRY:
436 break;
437 default:
438 return FALSE;
439 }
440
441 /* Further conditions before we actually do auto-completion: */
442 /* There must be a blank entry */
443 if (blank_entry == NULL)
444 return FALSE;
445
446 /* we must be on the blank entry */
447 if (entry != blank_entry)
448 return FALSE;
449
450 /* and leaving the description cell */
451 if (!gnc_cell_name_equal (cell_name, ENTRY_DESC_CELL))
452 return FALSE;
453
454 /* nothing but the date and description should be changed */
455 /* FIXME, this should be refactored. */
456 if (gnc_table_layout_get_cell_changed (ledger->table->layout,
457 ENTRY_ACTN_CELL, TRUE)
458 || gnc_table_layout_get_cell_changed (ledger->table->layout,
459 ENTRY_QTY_CELL, TRUE)
460 || gnc_table_layout_get_cell_changed (ledger->table->layout,
461 ENTRY_PRIC_CELL, TRUE)
462 || gnc_table_layout_get_cell_changed (ledger->table->layout,
463 ENTRY_DISC_CELL, TRUE)
464 || gnc_table_layout_get_cell_changed (ledger->table->layout,
465 ENTRY_DISTYPE_CELL, TRUE)
466 || gnc_table_layout_get_cell_changed (ledger->table->layout,
467 ENTRY_DISHOW_CELL, TRUE)
468 || gnc_table_layout_get_cell_changed (ledger->table->layout,
469 ENTRY_IACCT_CELL, TRUE)
470 || gnc_table_layout_get_cell_changed (ledger->table->layout,
471 ENTRY_BACCT_CELL, TRUE)
472 || gnc_table_layout_get_cell_changed (ledger->table->layout,
473 ENTRY_TAXABLE_CELL, TRUE)
474 || gnc_table_layout_get_cell_changed (ledger->table->layout,
475 ENTRY_TAXINCLUDED_CELL, TRUE)
476 || gnc_table_layout_get_cell_changed (ledger->table->layout,
477 ENTRY_TAXTABLE_CELL, TRUE)
478 || gnc_table_layout_get_cell_changed (ledger->table->layout,
479 ENTRY_VALUE_CELL, TRUE)
480 || gnc_table_layout_get_cell_changed (ledger->table->layout,
481 ENTRY_TAXVAL_CELL, TRUE)
482 || gnc_table_layout_get_cell_changed (ledger->table->layout,
483 ENTRY_BILLABLE_CELL, TRUE)
484 || gnc_table_layout_get_cell_changed (ledger->table->layout,
485 ENTRY_PAYMENT_CELL, TRUE))
486 return FALSE;
487
488 /* and the description should indeed be changed */
489 if (!gnc_table_layout_get_cell_changed (ledger->table->layout,
490 ENTRY_DESC_CELL, TRUE))
491 return FALSE;
492
493 /* to a non-empty value */
494 desc = gnc_table_layout_get_cell_value (ledger->table->layout, ENTRY_DESC_CELL);
495 if ((desc == NULL) || (*desc == '\0'))
496 return FALSE;
497
498 /* Ok, we are sure we want to trigger auto-completion. Now find an
499 * entry to copy the values from. */
500 auto_entry =
501 /* Use this for book-wide auto-completion of the invoice entries */
502 find_entry_in_book_by_desc(ledger, desc);
503
504 if (auto_entry == NULL)
505 return FALSE;
506
507 /* now perform the completion */
508 gnc_suspend_gui_refresh ();
509
510 /* Auto-complete the action field */
511 cell = gnc_table_layout_get_cell (ledger->table->layout, ENTRY_ACTN_CELL);
512 set_value_combo_cell (cell, gncEntryGetAction (auto_entry));
513
514 /* Auto-complete the account field */
515 switch (ledger->type)
516 {
517 case GNCENTRY_INVOICE_ENTRY:
518 case GNCENTRY_CUST_CREDIT_NOTE_ENTRY:
519 cell = gnc_table_layout_get_cell (ledger->table->layout, ENTRY_IACCT_CELL);
520 account_name = gnc_get_account_name_for_register (gncEntryGetInvAccount(auto_entry));
521 break;
522 case GNCENTRY_EXPVOUCHER_ENTRY:
523 case GNCENTRY_BILL_ENTRY:
524 case GNCENTRY_VEND_CREDIT_NOTE_ENTRY:
525 case GNCENTRY_EMPL_CREDIT_NOTE_ENTRY:
526 cell = gnc_table_layout_get_cell (ledger->table->layout, ENTRY_BACCT_CELL);
527 account_name = gnc_get_account_name_for_register (gncEntryGetBillAccount(auto_entry));
528 break;
529 case GNCENTRY_ORDER_ENTRY:
530 default:
531 cell = NULL;
532 account_name = NULL;
533 break;
534 }
535 set_value_combo_cell (cell, account_name);
536 g_free (account_name);
537
538 /* Auto-complete quantity cell. Note that this requires some care because
539 * credit notes store quantities with a reversed sign. So we need to figure
540 * out if the original document from which we extract the autofill entry
541 * was a credit note or not. */
542 {
543 gboolean orig_is_cn;
544 switch (ledger->type)
545 {
546 case GNCENTRY_INVOICE_ENTRY:
547 case GNCENTRY_CUST_CREDIT_NOTE_ENTRY:
548 orig_is_cn = gncInvoiceGetIsCreditNote (gncEntryGetInvoice (auto_entry));
549 break;
550 default:
551 orig_is_cn = gncInvoiceGetIsCreditNote (gncEntryGetBill (auto_entry));
552 break;
553 }
554 cell = gnc_table_layout_get_cell (ledger->table->layout, ENTRY_QTY_CELL);
555 set_value_price_cell (cell, gncEntryGetDocQuantity (auto_entry, orig_is_cn));
556 }
557
558 /* Auto-complete price cell */
559 {
560 gnc_numeric price;
561 switch (ledger->type)
562 {
563 case GNCENTRY_INVOICE_ENTRY:
564 case GNCENTRY_CUST_CREDIT_NOTE_ENTRY:
565 price = gncEntryGetInvPrice (auto_entry);
566 break;
567 default:
568 price = gncEntryGetBillPrice (auto_entry);
569 break;
570 }
571
572 /* Auto-complete price cell */
573 cell = gnc_table_layout_get_cell (ledger->table->layout, ENTRY_PRIC_CELL);
574 set_value_price_cell (cell, price);
575 }
576
577 /* We intentionally skip the discount column */
578
579 /* Taxable?, Tax-include?, Tax table */
580 {
581 gboolean taxable = FALSE, taxincluded = FALSE;
582 GncTaxTable *taxtable = NULL;
583 switch (ledger->type)
584 {
585 case GNCENTRY_INVOICE_ENTRY:
586 case GNCENTRY_CUST_CREDIT_NOTE_ENTRY:
587 taxable = gncEntryGetInvTaxable (auto_entry);
588 taxincluded = gncEntryGetInvTaxIncluded (auto_entry);
589 taxtable = gncEntryGetInvTaxTable (auto_entry);
590 break;
591 case GNCENTRY_BILL_ENTRY:
592 case GNCENTRY_VEND_CREDIT_NOTE_ENTRY:
593 taxable = gncEntryGetBillTaxable (auto_entry);
594 taxincluded = gncEntryGetBillTaxIncluded (auto_entry);
595 taxtable = gncEntryGetBillTaxTable (auto_entry);
596 break;
597 default:
598 break;
599 }
600
601 switch (ledger->type)
602 {
603 case GNCENTRY_INVOICE_ENTRY:
604 case GNCENTRY_CUST_CREDIT_NOTE_ENTRY:
605 case GNCENTRY_BILL_ENTRY:
606 case GNCENTRY_VEND_CREDIT_NOTE_ENTRY:
607 /* Taxable? cell */
608 cell = gnc_table_layout_get_cell (ledger->table->layout, ENTRY_TAXABLE_CELL);
609 gnc_checkbox_cell_set_flag ((CheckboxCell *) cell, taxable);
610 gnc_basic_cell_set_changed (cell, TRUE);
611
612 /* taxincluded? cell */
613 cell = gnc_table_layout_get_cell (ledger->table->layout, ENTRY_TAXINCLUDED_CELL);
614 gnc_checkbox_cell_set_flag ((CheckboxCell *) cell, taxincluded);
615 gnc_basic_cell_set_changed (cell, TRUE);
616
617 /* Taxable? cell */
618 cell = gnc_table_layout_get_cell (ledger->table->layout, ENTRY_TAXTABLE_CELL);
619 set_value_combo_cell(cell, gncTaxTableGetName (taxtable));
620 break;
621 default:
622 break;
623 }
624 }
625
626
627 gnc_resume_gui_refresh ();
628
629 /* now move to the non-empty amount column unless config setting says not */
630 if ( !gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL_REGISTER,
631 GNC_PREF_TAB_TRANS_MEMORISED) )
632 {
633 VirtualLocation new_virt_loc;
634 const char *cell_name = ENTRY_QTY_CELL;
635
636 if (gnc_table_get_current_cell_location (ledger->table, cell_name,
637 &new_virt_loc))
638 *p_new_virt_loc = new_virt_loc;
639 }
640
641 return TRUE;
642}
643
644static gboolean gnc_entry_ledger_traverse (VirtualLocation *p_new_virt_loc,
645 gncTableTraversalDir dir,
646 gpointer user_data)
647{
648 GncEntryLedger *ledger = user_data;
649 GncEntry *entry, *new_entry;
650 gint response;
651 VirtualLocation virt_loc;
652 int changed;
653 char const *cell_name;
654 gboolean exact_traversal;
655
656 if (!ledger) return FALSE;
657
658 exact_traversal = (dir == GNC_TABLE_TRAVERSE_POINTER);
659
660 entry = gnc_entry_ledger_get_current_entry (ledger);
661 if (!entry)
662 return FALSE;
663
664 /* no changes, make sure we aren't going off the end */
665 changed = gnc_table_current_cursor_changed (ledger->table, FALSE);
666 if (!changed)
667 return FALSE;
668
669 virt_loc = *p_new_virt_loc;
670
671 cell_name = gnc_table_get_current_cell_name (ledger->table);
672
673 /* See if we are leaving the account field */
674 do
675 {
676 ComboCell *cell;
677 char *name;
678 char *cell_name = NULL;
679
680 switch (ledger->type)
681 {
682 case GNCENTRY_INVOICE_ENTRY:
683 case GNCENTRY_INVOICE_VIEWER:
684 case GNCENTRY_CUST_CREDIT_NOTE_ENTRY:
685 case GNCENTRY_CUST_CREDIT_NOTE_VIEWER:
686 cell_name = ENTRY_IACCT_CELL;
687 break;
688 case GNCENTRY_BILL_ENTRY:
689 case GNCENTRY_BILL_VIEWER:
690 case GNCENTRY_EXPVOUCHER_ENTRY:
691 case GNCENTRY_EXPVOUCHER_VIEWER:
692 case GNCENTRY_VEND_CREDIT_NOTE_ENTRY:
693 case GNCENTRY_VEND_CREDIT_NOTE_VIEWER:
694 case GNCENTRY_EMPL_CREDIT_NOTE_ENTRY:
695 case GNCENTRY_EMPL_CREDIT_NOTE_VIEWER:
696 cell_name = ENTRY_BACCT_CELL;
697 break;
698 default:
699 g_warning ("Unhandled ledger type");
700 break;
701 }
702
703 if (!cell_name)
704 break;
705
706 if (!gnc_cell_name_equal (cell_name, cell_name))
707 break;
708
709 if (!gnc_table_layout_get_cell_changed (ledger->table->layout,
710 cell_name, FALSE))
711 break;
712
713 cell = (ComboCell *) gnc_table_layout_get_cell (ledger->table->layout,
714 cell_name);
715 if (!cell)
716 break;
717
718 name = cell->cell.value;
719 if (!name || *name == '\0')
720 break;
721
722 /* Create the account if necessary. Also checks for a placeholder */
723 if (!gnc_entry_ledger_get_account_by_name (ledger, (BasicCell *) cell,
724 cell->cell.value,
725 &ledger->full_refresh))
726 return TRUE;
727
728 }
729 while (FALSE);
730
731
732 /* See if we are leaving the TaxTable field */
733 do
734 {
735 ComboCell *cell;
737 char *name;
738
739 if (!gnc_cell_name_equal (cell_name, ENTRY_TAXTABLE_CELL))
740 break;
741
742 if (!gnc_table_layout_get_cell_changed (ledger->table->layout,
743 ENTRY_TAXTABLE_CELL, FALSE))
744 break;
745
746 cell = (ComboCell *) gnc_table_layout_get_cell (ledger->table->layout,
747 ENTRY_TAXTABLE_CELL);
748 if (!cell)
749 break;
750
751 name = cell->cell.value;
752 if (!name || *name == '\0')
753 break;
754
755 table = gncTaxTableLookupByName (ledger->book, cell->cell.value);
756 if (table)
757 break;
758
759 {
760 const char *format = _("The tax table %s does not exist. "
761 "Would you like to create it?");
762 if (!gnc_verify_dialog (GTK_WINDOW (ledger->parent), TRUE, format, name))
763 break;
764 }
765
766 ledger->full_refresh = FALSE;
767
768 table = gnc_ui_tax_table_new_from_name (GTK_WINDOW (ledger->parent), ledger->book, name);
769 if (!table)
770 break;
771
772 ledger->full_refresh = TRUE;
773
774 name = (char *)gncTaxTableGetName (table);
775 gnc_combo_cell_set_value (cell, name);
776 gnc_basic_cell_set_changed (&cell->cell, TRUE);
777
778 }
779 while (FALSE);
780
781
782 /* See if we are tabbing off the end of the very last line
783 * (i.e. the blank entry)
784 */
785 do
786 {
787 VirtualLocation virt_loc;
788
789 if (!changed && !ledger->blank_entry_edited)
790 break;
791
792 if (dir != GNC_TABLE_TRAVERSE_RIGHT)
793 break;
794
795 virt_loc = ledger->table->current_cursor_loc;
796 if (gnc_table_move_vertical_position (ledger->table, &virt_loc, 1))
797 break;
798
799 virt_loc = ledger->table->current_cursor_loc;
800 if (gnc_table_move_tab (ledger->table, &virt_loc, TRUE))
801 break;
802
803 *p_new_virt_loc = ledger->table->current_cursor_loc;
804
805 /* Yep, we're trying to leave the blank entry -- make sure
806 * we are allowed to do so by verifying the current cursor.
807 * If the current cursor is ok, then move on!
808 */
809
810 /* Verify that the cursor is ok. If we can't save the cell, don't move! */
811 if (!gnc_entry_ledger_verify_can_save (ledger))
812 {
813 return TRUE;
814 }
815
816 (p_new_virt_loc->vcell_loc.virt_row)++;
817 p_new_virt_loc->phys_row_offset = 0;
818 p_new_virt_loc->phys_col_offset = 0;
819
820 ledger->traverse_to_new = TRUE;
821
822 /* If we're here, we're tabbing off the end of the 'blank entry' */
823 return FALSE;
824
825 }
826 while (FALSE);
827
828 /* Now see if we are changing cursors. If not, we may be able to
829 * auto-complete. */
830 if (!gnc_table_virtual_cell_out_of_bounds (ledger->table,
831 virt_loc.vcell_loc))
832 {
833 if (gnc_entry_ledger_auto_completion (ledger, dir, p_new_virt_loc))
834 return FALSE;
835 }
836
837 /* Check for going off the end */
838 gnc_table_find_close_valid_cell (ledger->table, &virt_loc, exact_traversal);
839
840 /* Same entry, no problem -- we're just moving backwards in the cursor */
841 new_entry = gnc_entry_ledger_get_entry (ledger, virt_loc.vcell_loc);
842 if (entry == new_entry)
843 {
844 *p_new_virt_loc = virt_loc;
845
846 return FALSE;
847 }
848
849 /* If we are here, then we are trying to leave the cursor. Make sure
850 * the cursor we are leaving is valid. If so, ask the user if the
851 * changes should be recorded. If not, don't go anywhere.
852 */
853
854 /* Verify this cursor -- if it's not valid, don't let them move on */
855 if (!gnc_entry_ledger_verify_can_save (ledger))
856 {
857 *p_new_virt_loc = ledger->table->current_cursor_loc;
858 return TRUE;
859 }
860
861 /*
862 * XXX GNCENTRY_INVOICE_EDIT processing to be added:
863 * 1) check if the qty field changed.
864 * 2) if so, check if this entry is part of an order.
865 * 3) if so, ask if they want to change the entry or
866 * split the entry into two parts.
867 */
868
869 /* Ok, we are changing lines and the current entry has
870 * changed. We only ask them what they want to do in
871 * limited cases -- usually just let the change go through.
872 */
873 {
874 GtkWidget *dialog;
875 const char *title = _("Save the current entry?");
876 const char *message =
877 _("The current entry has been changed. However, this entry is "
878 "part of an existing order. Would you like to record the change "
879 "and effectively change your order?");
880
881 switch (ledger->type)
882 {
883 case GNCENTRY_INVOICE_ENTRY:
884 case GNCENTRY_CUST_CREDIT_NOTE_ENTRY:
885 if (gncEntryGetOrder (entry) != NULL)
886 {
887 dialog = gtk_message_dialog_new(GTK_WINDOW(ledger->parent),
888 GTK_DIALOG_DESTROY_WITH_PARENT,
889 GTK_MESSAGE_QUESTION,
890 GTK_BUTTONS_NONE,
891 "%s", title);
892 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
893 "%s", message);
894 gtk_dialog_add_buttons(GTK_DIALOG(dialog),
895 _("_Don't Record"), GTK_RESPONSE_REJECT,
896 _("_Cancel"), GTK_RESPONSE_CANCEL,
897 _("_Record"), GTK_RESPONSE_ACCEPT,
898 NULL);
899 response = gnc_dialog_run(GTK_DIALOG(dialog), GNC_PREF_WARN_INV_ENTRY_MOD);
900 gtk_widget_destroy(dialog);
901 break;
902 }
903
904 /* FALL THROUGH */
905 default:
906 response = GTK_RESPONSE_ACCEPT;
907 break;
908 }
909 }
910
911 switch (response)
912 {
913 case GTK_RESPONSE_ACCEPT:
914 break;
915
916 case GTK_RESPONSE_REJECT:
917 {
918 VirtualCellLocation vcell_loc;
919 GncEntry *new_entry;
920
921 new_entry = gnc_entry_ledger_get_entry (ledger, virt_loc.vcell_loc);
922
923 gnc_entry_ledger_cancel_cursor_changes (ledger);
924
925 if (gnc_entry_ledger_find_entry (ledger, new_entry, &vcell_loc))
926 virt_loc.vcell_loc = vcell_loc;
927
928 gnc_table_find_close_valid_cell (ledger->table, &virt_loc,
929 exact_traversal);
930
931 *p_new_virt_loc = virt_loc;
932 }
933
934 break;
935
936 case GTK_RESPONSE_CANCEL:
937 default:
938 return TRUE;
939 }
940
941 return FALSE;
942}
943
944TableControl * gnc_entry_ledger_control_new (void)
945{
946 TableControl * control;
947
948 control = gnc_table_control_new ();
949 control->move_cursor = gnc_entry_ledger_move_cursor;
950 control->traverse = gnc_entry_ledger_traverse;
951
952 return control;
953}
954
955
956void gnc_entry_ledger_cancel_cursor_changes (GncEntryLedger *ledger)
957{
958 VirtualLocation virt_loc;
959
960 if (ledger == NULL)
961 return;
962
963 virt_loc = ledger->table->current_cursor_loc;
964
965 if (!gnc_table_current_cursor_changed (ledger->table, FALSE))
966 return;
967
968 /* When cancelling edits, reload the cursor from the entry. */
969 gnc_table_clear_current_cursor_changes (ledger->table);
970
971 if (gnc_table_find_close_valid_cell (ledger->table, &virt_loc, FALSE))
972 gnc_table_move_cursor_gui (ledger->table, virt_loc);
973
974 gnc_table_refresh_gui (ledger->table, TRUE);
975}
976
977static gboolean
978gnc_entry_ledger_check_close_internal (GtkWidget *parent,
979 GncEntryLedger *ledger,
980 gboolean dontask)
981{
982 const char *message = _("The current entry has been changed. "
983 "Would you like to save it?");
984 VirtualLocation virt_loc;
985
986 virt_loc = ledger->table->current_cursor_loc;
987
988 if (gnc_entry_ledger_traverse (&virt_loc, GNC_TABLE_TRAVERSE_POINTER,
989 ledger))
990 return FALSE;
991
992 if (!gnc_entry_ledger_verify_can_save (ledger))
993 return FALSE;
994
995 if (dontask || gnc_verify_dialog (GTK_WINDOW (parent), TRUE, "%s", message))
996 gnc_entry_ledger_save (ledger, TRUE);
997 else
998 gnc_entry_ledger_cancel_cursor_changes (ledger);
999
1000 return TRUE;
1001}
1002
1003gboolean
1004gnc_entry_ledger_commit_entry (GncEntryLedger *ledger)
1005{
1006 if (!ledger) return TRUE;
1007
1008 return gnc_entry_ledger_check_close_internal (NULL, ledger, TRUE);
1009}
1010
1011gboolean
1012gnc_entry_ledger_check_close (GtkWidget *parent, GncEntryLedger *ledger)
1013{
1014 if (!ledger) return TRUE;
1015
1016 if (gnc_entry_ledger_changed (ledger))
1017 {
1018 gboolean dontask = FALSE;
1019
1020 if (ledger->type == GNCENTRY_INVOICE_ENTRY ||
1021 ledger->type == GNCENTRY_CUST_CREDIT_NOTE_ENTRY)
1022 {
1023 gboolean inv_value;
1024 gboolean only_inv_changed = FALSE;
1025
1026 if (gnc_table_current_cursor_changed (ledger->table, FALSE) == 1 &&
1027 gnc_table_layout_get_cell_changed (ledger->table->layout,
1028 ENTRY_INV_CELL, TRUE))
1029 only_inv_changed = TRUE;
1030
1031 inv_value = gnc_entry_ledger_get_checkmark (ledger, ENTRY_INV_CELL);
1032
1033 if (inv_value && only_inv_changed)
1034 {
1035 /* If the only change is that the 'inv' entry was clicked
1036 * "on", then just accept the change it without question.
1037 */
1038 dontask = TRUE;
1039 }
1040 }
1041
1042 return gnc_entry_ledger_check_close_internal (parent, ledger, dontask);
1043
1044 }
1045 return TRUE;
1046}
Account handling public routines.
This file contains the functions to present a gui to the user for creating a new account or editing a...
Generic api to store and retrieve preferences.
utility functions for the GnuCash UI
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.
gboolean gnc_entry_ledger_commit_entry(GncEntryLedger *ledger)
This will act just like hitting 'return' to record an entry.
GncEntry * gnc_entry_ledger_get_blank_entry(GncEntryLedger *ledger)
Exported Functions.
gboolean gnc_entry_ledger_check_close(GtkWidget *parent, GncEntryLedger *ledger)
This will ask the user if they really want to make a change.
gnc_numeric gnc_price_cell_get_value(PriceCell *cell)
return the value of a price cell
Definition pricecell.c:208
gboolean gnc_price_cell_set_value(PriceCell *cell, gnc_numeric amount)
updates amount, returns TRUE if string representation actually changed
Definition pricecell.c:219
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
Definition gnc-date.h:87
time64 gnc_time(time64 *tbuf)
get the current time
Definition gnc-date.cpp:262
GDate gncEntryGetDateGDate(const GncEntry *entry)
Returns the day of this entry.
Definition gncEntry.c:918
gnc_numeric gncEntryGetDocQuantity(const GncEntry *entry, gboolean is_cn)
Get the quantity as on the physical document.
Definition gncEntry.c:952
const GncGUID * guid_null(void)
Returns a GncGUID which is guaranteed to never reference any entity.
Definition guid.cpp:165
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 ...
void gncBillAddEntry(GncInvoice *bill, GncEntry *entry)
Call this function when adding an entry to a bill instead of an invoice.
Definition gncInvoice.c:719
gboolean gnc_numeric_zero_p(gnc_numeric a)
Returns 1 if the given gnc_numeric is 0 (zero), else returns 0.
gboolean gnc_numeric_equal(gnc_numeric a, gnc_numeric b)
Equivalence predicate: Returns TRUE (1) if a and b represent the same number.
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Get a boolean value from the preferences backend.
void qof_query_add_term(QofQuery *q, QofQueryParamList *param_list, QofQueryPredData *pred_data, QofQueryOp op)
This is the general function that adds a new Query Term to a query.
Definition qofquery.cpp:681
void qof_query_add_guid_match(QofQuery *q, QofQueryParamList *param_list, const GncGUID *guid, QofQueryOp op)
DOCUMENT ME !!
void qof_query_set_sort_increasing(QofQuery *q, gboolean prim_inc, gboolean sec_inc, gboolean tert_inc)
When a query is run, the results are sorted before being returned.
#define QOF_QUERY_FIRST_TERM
First/only term is same as 'and'.
Definition qofquery.h:102
void qof_query_set_book(QofQuery *query, QofBook *book)
Set the book to be searched.
void qof_query_set_sort_order(QofQuery *q, QofQueryParamList *params1, QofQueryParamList *params2, QofQueryParamList *params3)
When a query is run, the results are sorted before being returned.
void qof_query_destroy(QofQuery *query)
Frees the resources associate with a Query object.
GList * qof_query_run(QofQuery *query)
Perform the query, return the results.
void qof_query_set_max_results(QofQuery *q, int n)
Set the maximum number of results that should be returned.
void gnc_table_refresh_gui(Table *table, gboolean do_scroll)
Refresh the whole GUI from the table.
void gnc_table_move_cursor_gui(Table *table, VirtualLocation new_virt_loc)
will move the cursor and its GUI to the indicated location.
gboolean gnc_table_virtual_cell_out_of_bounds(Table *table, VirtualCellLocation vcell_loc)
checks the given location and returns true if it is out of bounds of the table.
gboolean gnc_table_find_close_valid_cell(Table *table, VirtualLocation *virt_loc, gboolean exact_pointer)
Find a close valid cell.
gboolean gnc_table_move_vertical_position(Table *table, VirtualLocation *virt_loc, int phys_row_offset)
Moves away from virtual location virt_loc by phys_row_offset physical rows.
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
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
A Query.
Definition qofquery.cpp:75
Declarations for the Table object.