1 /*
2 * gncEntryLedgerLoad.c -- a Ledger widget for entering GncEntry objects
3 * Copyright (C) 2001, 2002, 2003 Derek Atkins
4 * Author: Derek Atkins <warlord@MIT.EDU>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of
9 * the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, contact:
18 *
19 * Free Software Foundation Voice: +1-617-542-5942
20 * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
21 * Boston, MA 02110-1301, USA gnu@gnu.org
22 */
23
24 #include <config.h>
25
26 #include <glib.h>
27 #include <glib/gi18n.h>
28
29 #include "Account.h"
30 #include "account-quickfill.h"
31 #include "combocell.h"
32 #include "gnc-component-manager.h"
33 #include "gnc-prefs.h"
34 #include "gnc-ui-util.h"
35 #include "recncell.h"
36
37 #include "gncEntry.h"
38 #include "gncEntryLedger.h"
39 #include "gncEntryLedgerP.h"
40 #include "quickfillcell.h"
41 #include "gnc-entry-quickfill.h"
42
43 #define GNC_PREF_TAX_INCL "tax-included"
44
45 static const QofLogModule log_module = "Business Entry Ledger";
46
47 /* XXX: This should go elsewhere */
gnc_entry_ledger_type_string_getter(char flag)48 const char* gnc_entry_ledger_type_string_getter (char flag)
49 {
50 switch (flag)
51 {
52 case '1':
53 return _ ("$");
54 case '2':
55 return _ ("%");
56 default:
57 break;
58 };
59 return "?";
60 }
61
gnc_entry_ledger_how_string_getter(char flag)62 const char* gnc_entry_ledger_how_string_getter (char flag)
63 {
64 switch (flag)
65 {
66 case '1':
67 return _ ("<");
68 case '2':
69 return _ ("=");
70 case '3':
71 return _ (">");
72 default:
73 break;
74 };
75 return "?";
76 }
77
load_discount_type_cells(GncEntryLedger * ledger)78 static void load_discount_type_cells (GncEntryLedger* ledger)
79 {
80 RecnCell* cell;
81
82 if (!ledger) return;
83
84 cell = (RecnCell*)
85 gnc_table_layout_get_cell (ledger->table->layout, ENTRY_DISTYPE_CELL);
86
87 if (!cell) return;
88
89 gnc_recn_cell_set_valid_flags (cell, "12", '2');
90 gnc_recn_cell_set_flag_order (cell, "21");
91 gnc_recn_cell_set_string_getter (cell, gnc_entry_ledger_type_string_getter);
92 }
93
load_discount_how_cells(GncEntryLedger * ledger)94 static void load_discount_how_cells (GncEntryLedger* ledger)
95 {
96 RecnCell* cell;
97
98 if (!ledger) return;
99
100 cell = (RecnCell*)
101 gnc_table_layout_get_cell (ledger->table->layout, ENTRY_DISHOW_CELL);
102
103 if (!cell) return;
104
105 gnc_recn_cell_set_valid_flags (cell, "123", '1');
106 gnc_recn_cell_set_flag_order (cell, "123");
107 gnc_recn_cell_set_string_getter (cell, gnc_entry_ledger_how_string_getter);
108 }
109
load_payment_type_cells(GncEntryLedger * ledger)110 static void load_payment_type_cells (GncEntryLedger* ledger)
111 {
112 ComboCell* cell;
113 const GncOwner* owner;
114 GncEmployee* employee;
115
116 cell = (ComboCell*) gnc_table_layout_get_cell (ledger->table->layout,
117 ENTRY_PAYMENT_CELL);
118 if (!cell) return;
119
120 if (!ledger->invoice) return;
121
122 owner = gncOwnerGetEndOwner (gncInvoiceGetOwner (ledger->invoice));
123 if (gncOwnerGetType (owner) != GNC_OWNER_EMPLOYEE)
124 return;
125
126 employee = gncOwnerGetEmployee (owner);
127 g_return_if_fail (employee);
128
129 gnc_combo_cell_clear_menu (cell);
130 gnc_combo_cell_add_menu_item (cell, _ ("Cash"));
131
132 if (gncEmployeeGetCCard (employee))
133 gnc_combo_cell_add_menu_item (cell, _ ("Charge"));
134 }
135
136 /* ==================================================================== */
137 /* Return TRUE if we don't want to add this account to the xfer menu */
138
139 static gboolean
skip_expense_acct_cb(Account * account,gpointer user_data)140 skip_expense_acct_cb (Account* account, gpointer user_data)
141 {
142 GNCAccountType type;
143
144 /* Don't add A/R, A/P, Bank, Cash, or Equity accounts */
145 type = xaccAccountGetType (account);
146 if (type == ACCT_TYPE_PAYABLE || type == ACCT_TYPE_RECEIVABLE ||
147 type == ACCT_TYPE_CASH || type == ACCT_TYPE_BANK ||
148 type == ACCT_TYPE_EQUITY || type == ACCT_TYPE_TRADING)
149 {
150 return TRUE;
151 }
152
153 /* If this is an ORDER or INVOICE, then leave out the expenses. */
154 if (type == ACCT_TYPE_EXPENSE) return TRUE;
155
156 /* Don't add placeholder accounts */
157 if (xaccAccountGetPlaceholder (account)) return TRUE;
158
159 return FALSE;
160 }
161
162 static gboolean
skip_income_acct_cb(Account * account,gpointer user_data)163 skip_income_acct_cb (Account* account, gpointer user_data)
164 {
165 GNCAccountType type;
166
167 /* Don't add A/R, A/P, Bank, Cash, or Equity accounts */
168 type = xaccAccountGetType (account);
169 if (type == ACCT_TYPE_PAYABLE || type == ACCT_TYPE_RECEIVABLE ||
170 type == ACCT_TYPE_CASH || type == ACCT_TYPE_BANK ||
171 type == ACCT_TYPE_EQUITY || type == ACCT_TYPE_TRADING)
172 {
173 return TRUE;
174 }
175
176 /* If this is a BILL, then leave out the incomes */
177 if (type == ACCT_TYPE_INCOME) return TRUE;
178
179 /* Don't add placeholder accounts */
180 if (xaccAccountGetPlaceholder (account)) return TRUE;
181
182 return FALSE;
183 }
184
185 /* ===================================================================== */
186 /* Splat the account name into the transfer cell combobox menu */
187
188 #define EKEY "Expense Business entry quickfill"
189 #define IKEY "Income Business entry quickfill"
190
191 static void
load_xfer_type_cells(GncEntryLedger * ledger)192 load_xfer_type_cells (GncEntryLedger* ledger)
193 {
194 Account* root;
195 ComboCell* cell;
196 QuickFill* qf = NULL;
197 GtkListStore* store = NULL;
198
199 root = gnc_book_get_root_account (ledger->book);
200 if (root == NULL) return;
201
202 /* Use a common, shared quickfill. For the ORDER or INVOICE,
203 * ledgers, we don't want expense-type accounts in the menu.
204 * For BILL, etc. then leave out the income types.
205 */
206 switch (ledger->type)
207 {
208 case GNCENTRY_ORDER_ENTRY:
209 case GNCENTRY_ORDER_VIEWER:
210 case GNCENTRY_INVOICE_ENTRY:
211 case GNCENTRY_INVOICE_VIEWER:
212 case GNCENTRY_CUST_CREDIT_NOTE_ENTRY:
213 case GNCENTRY_CUST_CREDIT_NOTE_VIEWER:
214 qf = gnc_get_shared_account_name_quickfill (root, IKEY,
215 skip_expense_acct_cb, NULL);
216 store = gnc_get_shared_account_name_list_store (root, IKEY,
217 skip_expense_acct_cb, NULL);
218 break;
219
220 case GNCENTRY_BILL_ENTRY:
221 case GNCENTRY_BILL_VIEWER:
222 case GNCENTRY_EXPVOUCHER_ENTRY:
223 case GNCENTRY_EXPVOUCHER_VIEWER:
224 case GNCENTRY_VEND_CREDIT_NOTE_ENTRY:
225 case GNCENTRY_VEND_CREDIT_NOTE_VIEWER:
226 case GNCENTRY_EMPL_CREDIT_NOTE_ENTRY:
227 case GNCENTRY_EMPL_CREDIT_NOTE_VIEWER:
228 case GNCENTRY_NUM_REGISTER_TYPES:
229 qf = gnc_get_shared_account_name_quickfill (root, EKEY,
230 skip_income_acct_cb, NULL);
231 store = gnc_get_shared_account_name_list_store (root, EKEY,
232 skip_income_acct_cb,
233 NULL);
234 break;
235 default:
236 PWARN ("Bad GncEntryLedgerType");
237 break;
238 }
239
240 cell = (ComboCell*)
241 gnc_table_layout_get_cell (ledger->table->layout, ENTRY_IACCT_CELL);
242 gnc_combo_cell_use_quickfill_cache (cell, qf);
243 gnc_combo_cell_use_list_store_cache (cell, store);
244
245 cell = (ComboCell*)
246 gnc_table_layout_get_cell (ledger->table->layout, ENTRY_BACCT_CELL);
247 gnc_combo_cell_use_quickfill_cache (cell, qf);
248 gnc_combo_cell_use_list_store_cache (cell, store);
249 }
250
251 /* ===================================================================== */
252
load_taxtable_type_cells(GncEntryLedger * ledger)253 static void load_taxtable_type_cells (GncEntryLedger* ledger)
254 {
255 GList* list;
256 ComboCell* cell;
257
258 cell = (ComboCell*)
259 gnc_table_layout_get_cell (ledger->table->layout, ENTRY_TAXTABLE_CELL);
260 gnc_combo_cell_clear_menu (cell);
261
262 list = gncTaxTableGetTables (ledger->book);
263 for (; list ; list = list->next)
264 {
265 GncTaxTable* table = list->data;
266 const char* name = gncTaxTableGetName (table);
267 if (name != NULL)
268 gnc_combo_cell_add_menu_item (cell, (char*)name);
269 }
270 }
271
272 static void
gnc_entry_ledger_show_entry(GncEntryLedger * ledger,VirtualCellLocation start_loc)273 gnc_entry_ledger_show_entry (GncEntryLedger* ledger,
274 VirtualCellLocation start_loc)
275 {
276 VirtualCellLocation end_loc;
277 int v_row;
278
279 end_loc = start_loc;
280 v_row = end_loc.virt_row + 1;
281 end_loc.virt_row = MIN (v_row, ledger->table->num_virt_rows - 1);
282
283 gnc_table_show_range (ledger->table, start_loc, end_loc);
284 }
285
286 #define DESC_QF_KEY_INVOICES "ENTRY_DESC_CELL_QF_INVOICES"
287 #define DESC_QF_KEY_BILLS "ENTRY_DESC_CELL_QF_BILLS"
288
289 static void
load_description_cell(GncEntryLedger * ledger)290 load_description_cell (GncEntryLedger* ledger)
291 {
292 QuickFill* shared_quickfill;
293 QuickFillCell* cell;
294
295 switch (ledger->type)
296 {
297 case GNCENTRY_INVOICE_ENTRY:
298 case GNCENTRY_INVOICE_VIEWER:
299 case GNCENTRY_CUST_CREDIT_NOTE_ENTRY:
300 case GNCENTRY_CUST_CREDIT_NOTE_VIEWER:
301 shared_quickfill = gnc_get_shared_entry_desc_quickfill (ledger->book,
302 DESC_QF_KEY_INVOICES, TRUE);
303 break;
304 default:
305 shared_quickfill = gnc_get_shared_entry_desc_quickfill (ledger->book,
306 DESC_QF_KEY_BILLS, FALSE);
307 break;
308 };
309
310 cell = (QuickFillCell*)
311 gnc_table_layout_get_cell (ledger->table->layout, ENTRY_DESC_CELL);
312 gnc_quickfill_cell_use_quickfill_cache (cell, shared_quickfill);
313 }
314
gnc_entry_ledger_load_xfer_cells(GncEntryLedger * ledger)315 void gnc_entry_ledger_load_xfer_cells (GncEntryLedger* ledger)
316 {
317 load_xfer_type_cells (ledger);
318 load_taxtable_type_cells (ledger);
319 load_payment_type_cells (ledger);
320 load_description_cell (ledger);
321 }
322
323 /* XXX (FIXME): This should be in a config file! */
324 /* Copy GncEntry information from the list to the rows of the Ledger. */
325 /* XXX This code is a cut-n-paste job from the SplitRegister code;
326 * the split-register should be generalized to the point where a cut-n-paste
327 * like this isn't required, and this should be trashed.
328 */
gnc_entry_ledger_load(GncEntryLedger * ledger,GList * entry_list)329 void gnc_entry_ledger_load (GncEntryLedger* ledger, GList* entry_list)
330 {
331 GncEntry* blank_entry, *find_entry;
332 CursorBuffer* cursor_buffer;
333 Table* table;
334
335 GList* node;
336 CellBlock* cursor_header, *cursor;
337 VirtualCellLocation vcell_loc;
338 VirtualLocation save_loc;
339 gboolean start_primary_color = TRUE;
340
341 int new_entry_row = -1;
342
343 if (!ledger) return;
344
345 /* Load up cells */
346 load_discount_type_cells (ledger);
347 load_discount_how_cells (ledger);
348 gnc_entry_ledger_load_xfer_cells (ledger);
349
350 blank_entry = gnc_entry_ledger_get_blank_entry (ledger);
351
352 if (blank_entry == NULL && ledger->invoice == NULL && entry_list == NULL)
353 return;
354
355 if (blank_entry == NULL && ledger->invoice)
356 {
357 switch (ledger->type)
358 {
359 case GNCENTRY_ORDER_ENTRY:
360 case GNCENTRY_INVOICE_ENTRY:
361 case GNCENTRY_BILL_ENTRY:
362 case GNCENTRY_EXPVOUCHER_ENTRY:
363 case GNCENTRY_CUST_CREDIT_NOTE_ENTRY:
364 case GNCENTRY_VEND_CREDIT_NOTE_ENTRY:
365 case GNCENTRY_EMPL_CREDIT_NOTE_ENTRY:
366
367 gnc_suspend_gui_refresh();
368
369 blank_entry = gncEntryCreate (ledger->book);
370 gncEntrySetDateGDate (blank_entry, &ledger->last_date_entered);
371 ledger->blank_entry_guid = *gncEntryGetGUID (blank_entry);
372
373 gnc_resume_gui_refresh();
374
375 /* The rest of this does not apply to expense vouchers */
376 if (ledger->type != GNCENTRY_EXPVOUCHER_ENTRY)
377 {
378 const GncOwner* owner = gncOwnerGetEndOwner (gncInvoiceGetOwner (
379 ledger->invoice));
380 GncTaxTable* table = NULL;
381 GncTaxIncluded taxincluded_p = GNC_TAXINCLUDED_USEGLOBAL;
382 gboolean taxincluded = FALSE;
383 gnc_numeric discount = gnc_numeric_zero();
384 gnc_numeric price = gnc_numeric_zero();
385
386 /* Determine the Price from Customer's or Vendor's Job */
387 switch (gncOwnerGetType (gncInvoiceGetOwner (ledger->invoice)))
388 {
389 case GNC_OWNER_JOB:
390 price = gncJobGetRate (gncOwnerGetJob (gncInvoiceGetOwner (ledger->invoice)));
391 break;
392 default:
393 break;
394 }
395
396 /* Determine the TaxIncluded and Discount values */
397 switch (gncOwnerGetType (owner))
398 {
399 case GNC_OWNER_CUSTOMER:
400 taxincluded_p = gncCustomerGetTaxIncluded (owner->owner.customer);
401 discount = gncCustomerGetDiscount (owner->owner.customer);
402 break;
403 case GNC_OWNER_VENDOR:
404 taxincluded_p = gncVendorGetTaxIncluded (owner->owner.vendor);
405 break;
406 default:
407 break;
408 }
409
410 /* Compute the default taxincluded */
411 switch (taxincluded_p)
412 {
413 case GNC_TAXINCLUDED_YES:
414 taxincluded = TRUE;
415 break;
416 case GNC_TAXINCLUDED_NO:
417 taxincluded = FALSE;
418 break;
419 case GNC_TAXINCLUDED_USEGLOBAL:
420 if (ledger->prefs_group)
421 {
422 taxincluded = gnc_prefs_get_bool (ledger->prefs_group, GNC_PREF_TAX_INCL);
423 }
424 else
425 {
426 taxincluded = FALSE;
427 }
428 break;
429 }
430
431 /* Compute the proper taxtable */
432 switch (gncOwnerGetType (owner))
433 {
434 case GNC_OWNER_CUSTOMER:
435 table = gncTaxTableGetDefault (ledger->book,
436 GNC_OWNER_CUSTOMER);
437
438 if (gncCustomerGetTaxTableOverride (owner->owner.customer))
439 table = gncCustomerGetTaxTable (owner->owner.customer);
440 break;
441
442 case GNC_OWNER_VENDOR:
443 table = gncTaxTableGetDefault (ledger->book,
444 GNC_OWNER_VENDOR);
445
446 if (gncVendorGetTaxTableOverride (owner->owner.vendor))
447 table = gncVendorGetTaxTable (owner->owner.vendor);
448 break;
449
450 default:
451 break;
452 }
453
454 if (ledger->is_cust_doc)
455 {
456 gncEntrySetInvTaxable (blank_entry, table != NULL);
457 gncEntrySetInvTaxTable (blank_entry, table);
458 gncEntrySetInvTaxIncluded (blank_entry, taxincluded);
459 gncEntrySetInvDiscount (blank_entry, discount);
460 gncEntrySetInvPrice (blank_entry, price);
461 }
462 else
463 {
464 gncEntrySetBillTaxable (blank_entry, table != NULL);
465 gncEntrySetBillTaxTable (blank_entry, table);
466 gncEntrySetBillTaxIncluded (blank_entry, taxincluded);
467 gncEntrySetBillPrice (blank_entry, price);
468 }
469 }
470
471 break;
472 default:
473 ledger->blank_entry_guid = *guid_null();
474 break;
475 }
476 ledger->blank_entry_edited = FALSE;
477 }
478
479 table = ledger->table;
480
481 gnc_table_leave_update (table, table->current_cursor_loc);
482 save_loc = table->current_cursor_loc;
483
484 /* Figure out where we are going to */
485 if (ledger->traverse_to_new)
486 {
487 find_entry = blank_entry;
488 }
489 else if (ledger->hint_entry)
490 {
491 find_entry = ledger->hint_entry;
492 }
493 else
494 {
495 find_entry = gnc_entry_ledger_get_current_entry (ledger);
496 /* XXX: get current entry (cursor_hint_xxx) */
497 }
498
499 /* If the current cursor has changed we save the values for later
500 * possible restoration. */
501 if (gnc_table_current_cursor_changed (table, TRUE) &&
502 (find_entry == gnc_entry_ledger_get_current_entry (ledger)))
503 {
504 cursor_buffer = gnc_cursor_buffer_new();
505 gnc_table_save_current_cursor (table, cursor_buffer);
506 }
507 else
508 cursor_buffer = NULL;
509
510 /* disable move callback -- we don't want the cascade of
511 * callbacks while we are fiddling with loading the register */
512 gnc_table_control_allow_move (table->control, FALSE);
513
514 /* invalidate the cursor */
515 {
516 VirtualLocation virt_loc;
517
518 virt_loc.vcell_loc.virt_row = -1;
519 virt_loc.vcell_loc.virt_col = -1;
520 virt_loc.phys_row_offset = -1;
521 virt_loc.phys_col_offset = -1;
522
523 gnc_table_move_cursor_gui (table, virt_loc);
524 }
525
526 /* make sure that the header is loaded */
527 vcell_loc.virt_row = 0;
528 vcell_loc.virt_col = 0;
529 cursor_header = gnc_table_layout_get_cursor (table->layout, CURSOR_HEADER);
530 gnc_table_set_vcell (table, cursor_header, NULL, TRUE, TRUE, vcell_loc);
531 vcell_loc.virt_row++;
532
533 /* get the current time and reset the dividing row */
534 table->model->dividing_row_upper = -1;
535 table->model->dividing_row = -1;
536 table->model->dividing_row_lower = -1;
537 cursor = gnc_table_layout_get_cursor (table->layout, "cursor");
538
539 /* Populate the table */
540 for (node = entry_list; node; node = node->next)
541 {
542 GncEntry* entry = node->data;
543
544 /* Don't load the blank entry */
545 if (entry == blank_entry)
546 continue;
547
548 /* If this is the first load of the ledger, fill the quickfill cells */
549 {
550 /* XXX */
551 }
552
553 if (entry == find_entry)
554 new_entry_row = vcell_loc.virt_row;
555
556 gnc_table_set_vcell (table, cursor, gncEntryGetGUID (entry),
557 TRUE, start_primary_color, vcell_loc);
558 vcell_loc.virt_row++;
559
560 /* Flip color for the next guy */
561 start_primary_color = !start_primary_color;
562 }
563
564 /* Add the blank entry at the end. */
565 if (blank_entry)
566 {
567 gnc_table_set_vcell (table, cursor, gncEntryGetGUID (blank_entry),
568 TRUE, start_primary_color, vcell_loc);
569
570 if (find_entry == blank_entry)
571 new_entry_row = vcell_loc.virt_row;
572
573 vcell_loc.virt_row++;
574 }
575
576 /* Resize the table */
577 gnc_table_set_size (table, vcell_loc.virt_row, 1);
578
579 /* Restore the cursor to its rightful position */
580 if (new_entry_row > 0)
581 save_loc.vcell_loc.virt_row = new_entry_row;
582
583 if (gnc_table_find_close_valid_cell (table, &save_loc, FALSE))
584 {
585 gnc_table_move_cursor_gui (table, save_loc);
586
587 if (find_entry == gnc_entry_ledger_get_current_entry (ledger))
588 gnc_table_restore_current_cursor (table, cursor_buffer);
589 }
590
591 gnc_cursor_buffer_destroy (cursor_buffer);
592 cursor_buffer = NULL;
593
594 /* Reset the ledger */
595 ledger->traverse_to_new = FALSE;
596 ledger->hint_entry = NULL;
597
598 /* Set the cell fractions */
599
600
601 gnc_table_refresh_gui (table, TRUE);
602 gnc_entry_ledger_show_entry (ledger, table->current_cursor_loc.vcell_loc);
603
604 /* Set completion character */
605 gnc_combo_cell_set_complete_char
606 ((ComboCell*)
607 gnc_table_layout_get_cell (table->layout, ENTRY_IACCT_CELL),
608 gnc_get_account_separator());
609
610 gnc_combo_cell_set_complete_char
611 ((ComboCell*)
612 gnc_table_layout_get_cell (table->layout, ENTRY_BACCT_CELL),
613 gnc_get_account_separator());
614
615 /* enable callback for cursor user-driven moves */
616 gnc_table_control_allow_move (table->control, TRUE);
617 }
618
619 /* =========================== END OF FILE ========================== */
620