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