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