/** Add a transaction to the register. * * Virtual cells are set up to hold the data, beginning at @a vcell_loc and * continuing downward in the same virtual column. The first virtual cell * will be a "leading" cell, followed by a virtual cell for each split of * transaction @a trans. For example, the "transaction journal" register * style keeps all the transaction-level data and totals in the leading * cell, and all split-level data in the cells that follow. * * Each of these cells will remember the GncGUID of the ::Split to which it * is associated. The leading virtual cell will be assigned the GncGUID of * the "anchoring split" specified by @a split. * * Optionally an extra, empty virtual cell will be assigned to no split. * This should not be confused with the "blank split", because this cell is * not tied to any split at all, not even the "blank split". A null GncGUID * is assigned to it. * * The caller can find out which virtual row was used for a particular virtual * cell by using parameters @a find_trans, @a find_split, and @a find_class. * If a match is found, the row is returned in @a new_split_row. Otherwise, * @a new_split_row is not changed. * - To find the virtual cell for a particular split, set @a find_split to * the target ::Split and @a find_class to ::CURSOR_CLASS_SPLIT. * - To find the empty row, set @a find_trans equal to @a trans, @a find_split * to @c NULL, and @a find_class to ::CURSOR_CLASS_SPLIT. * - The leading virtual cell is always placed in the row specified by * @a vcell_loc, but this will not be returned in @a new_split_row unless * @a find_split is set to the anchoring split and @a find_class is not * ::CURSOR_CLASS_SPLIT. * * @param reg a ::SplitRegister * * @param trans the transaction to add to the register * * @param split a ::Split to use as the "anchoring split" in the "leading" * virtual cell * * @param lead_cursor the cursor to use for the "leading" virtual cell * * @param split_cursor the cursor to use in the split rows that follow * * @param visible_splits @c TRUE to make the split rows visible, @c FALSE * otherwise * * @param start_primary_color @c TRUE to use the primary color for the * "leading" row, @c FALSE otherwise * * @param add_empty @c TRUE if an empty row should be added, @c FALSE * otherwise * * @param find_trans the transaction parameter for row searching * * @param find_split the split parameter for row searching * * @param find_class the cursor class parameter for row searching * * @param new_split_row a pointer to be filled with the matching virtual row. * * @param vcell_loc the location to begin setting virtual cells. The row * will be changed to the row below the last row filled. */ static void gnc_split_register_add_transaction (SplitRegister *reg, Transaction *trans, Split *split, CellBlock *lead_cursor, CellBlock *split_cursor, gboolean visible_splits, gboolean start_primary_color, gboolean add_empty, Transaction *find_trans, Split *find_split, CursorClass find_class, int *new_split_row, VirtualCellLocation *vcell_loc) { GList *node; g_return_if_fail(reg); g_return_if_fail(vcell_loc); if (split == find_split) *new_split_row = vcell_loc->virt_row; /* Set the "leading" virtual cell. */ gnc_table_set_vcell (reg->table, lead_cursor, xaccSplitGetGUID (split), TRUE, start_primary_color, *vcell_loc); vcell_loc->virt_row++; /* Continue setting up virtual cells in a column, using a row for each * split in the transaction. */ for (node = xaccTransGetSplitList (trans); node; node = node->next) { Split *secondary = node->data; if (!xaccTransStillHasSplit(trans, secondary)) continue; if (secondary == find_split && find_class == CURSOR_CLASS_SPLIT) *new_split_row = vcell_loc->virt_row; gnc_table_set_vcell (reg->table, split_cursor, xaccSplitGetGUID (secondary), visible_splits, TRUE, *vcell_loc); vcell_loc->virt_row++; } /* If requested, add an empty split row at the end. */ if (add_empty) { if (find_trans == trans && find_split == NULL && find_class == CURSOR_CLASS_SPLIT) *new_split_row = vcell_loc->virt_row; gnc_table_set_vcell(reg->table, split_cursor, xaccSplitGetGUID(NULL), FALSE, TRUE, *vcell_loc); vcell_loc->virt_row++; } }
/* XXX This code is a cut-n-paste job from the SplitRegister code; * the split-register should be generalized to the point where a cut-n-paste * like this isn't required, and this should be trashed. */ void gnc_entry_ledger_load (GncEntryLedger *ledger, GList *entry_list) { GncEntry *blank_entry, *find_entry; CursorBuffer *cursor_buffer; Table *table; GList *node; CellBlock *cursor_header, *cursor; VirtualCellLocation vcell_loc; VirtualLocation save_loc; gboolean start_primary_color = TRUE; int new_entry_row = -1; if (!ledger) return; /* Load up cells */ load_discount_type_cells (ledger); load_discount_how_cells (ledger); gnc_entry_ledger_load_xfer_cells (ledger); blank_entry = gnc_entry_ledger_get_blank_entry (ledger); if (blank_entry == NULL && ledger->invoice == NULL && entry_list == NULL) return; if (blank_entry == NULL && ledger->invoice) { switch (ledger->type) { case GNCENTRY_ORDER_ENTRY: case GNCENTRY_INVOICE_ENTRY: case GNCENTRY_BILL_ENTRY: case GNCENTRY_EXPVOUCHER_ENTRY: case GNCENTRY_CUST_CREDIT_NOTE_ENTRY: case GNCENTRY_VEND_CREDIT_NOTE_ENTRY: case GNCENTRY_EMPL_CREDIT_NOTE_ENTRY: gnc_suspend_gui_refresh (); blank_entry = gncEntryCreate (ledger->book); gncEntrySetDateGDate (blank_entry, &ledger->last_date_entered); ledger->blank_entry_guid = *gncEntryGetGUID (blank_entry); gnc_resume_gui_refresh (); /* The rest of this does not apply to expense vouchers */ if (ledger->type != GNCENTRY_EXPVOUCHER_ENTRY) { const GncOwner *owner = gncOwnerGetEndOwner (gncInvoiceGetOwner (ledger->invoice)); GncTaxTable *table = NULL; GncTaxIncluded taxincluded_p = GNC_TAXINCLUDED_USEGLOBAL; gboolean taxincluded = FALSE; gnc_numeric discount = gnc_numeric_zero (); gnc_numeric price = gnc_numeric_zero (); /* Determine the Price from Customer's or Vendor's Job */ switch (gncOwnerGetType (gncInvoiceGetOwner (ledger->invoice))) { case GNC_OWNER_JOB: price = gncJobGetRate( gncOwnerGetJob (gncInvoiceGetOwner (ledger->invoice))); break; default: break; } /* Determine the TaxIncluded and Discount values */ switch (gncOwnerGetType (owner)) { case GNC_OWNER_CUSTOMER: taxincluded_p = gncCustomerGetTaxIncluded (owner->owner.customer); discount = gncCustomerGetDiscount (owner->owner.customer); break; case GNC_OWNER_VENDOR: taxincluded_p = gncVendorGetTaxIncluded (owner->owner.vendor); break; default: break; } /* Compute the default taxincluded */ switch (taxincluded_p) { case GNC_TAXINCLUDED_YES: taxincluded = TRUE; break; case GNC_TAXINCLUDED_NO: taxincluded = FALSE; break; case GNC_TAXINCLUDED_USEGLOBAL: if (ledger->prefs_group) { taxincluded = gnc_prefs_get_bool (ledger->prefs_group, GNC_PREF_TAX_INCL); } else { taxincluded = FALSE; } break; } /* Compute the proper taxtable */ switch (gncOwnerGetType (owner)) { case GNC_OWNER_CUSTOMER: table = gnc_business_get_default_tax_table (ledger->book, GNC_OWNER_CUSTOMER); if (gncCustomerGetTaxTableOverride (owner->owner.customer)) table = gncCustomerGetTaxTable (owner->owner.customer); break; case GNC_OWNER_VENDOR: table = gnc_business_get_default_tax_table (ledger->book, GNC_OWNER_VENDOR); if (gncVendorGetTaxTableOverride (owner->owner.vendor)) table = gncVendorGetTaxTable (owner->owner.vendor); break; default: break; } if (ledger->is_cust_doc) { gncEntrySetInvTaxTable (blank_entry, table); gncEntrySetInvTaxIncluded (blank_entry, taxincluded); gncEntrySetInvDiscount (blank_entry, discount); gncEntrySetInvPrice (blank_entry, price); } else { gncEntrySetBillTaxTable (blank_entry, table); gncEntrySetBillTaxIncluded (blank_entry, taxincluded); gncEntrySetBillPrice (blank_entry, price); } } break; default: ledger->blank_entry_guid = *guid_null (); break; } ledger->blank_entry_edited = FALSE; } table = ledger->table; gnc_table_leave_update (table, table->current_cursor_loc); save_loc = table->current_cursor_loc; /* Figure out where we are going to */ if (ledger->traverse_to_new) { find_entry = blank_entry; } else if (ledger->hint_entry) { find_entry = ledger->hint_entry; } else { find_entry = gnc_entry_ledger_get_current_entry(ledger); /* XXX: get current entry (cursor_hint_xxx) */ } /* If the current cursor has changed we save the values for later * possible restoration. */ if (gnc_table_current_cursor_changed (table, TRUE) && (find_entry == gnc_entry_ledger_get_current_entry (ledger))) { cursor_buffer = gnc_cursor_buffer_new (); gnc_table_save_current_cursor (table, cursor_buffer); } else cursor_buffer = NULL; /* disable move callback -- we don't want the cascade of * callbacks while we are fiddling with loading the register */ gnc_table_control_allow_move (table->control, FALSE); /* invalidate the cursor */ { VirtualLocation virt_loc; virt_loc.vcell_loc.virt_row = -1; virt_loc.vcell_loc.virt_col = -1; virt_loc.phys_row_offset = -1; virt_loc.phys_col_offset = -1; gnc_table_move_cursor_gui (table, virt_loc); } /* make sure that the header is loaded */ vcell_loc.virt_row = 0; vcell_loc.virt_col = 0; cursor_header = gnc_table_layout_get_cursor (table->layout, CURSOR_HEADER); gnc_table_set_vcell (table, cursor_header, NULL, TRUE, TRUE, vcell_loc); vcell_loc.virt_row++; /* get the current time and reset the dividing row */ table->model->dividing_row_upper = -1; table->model->dividing_row = -1; table->model->dividing_row_lower = -1; cursor = gnc_table_layout_get_cursor (table->layout, "cursor"); /* Populate the table */ for (node = entry_list; node; node = node->next) { GncEntry *entry = node->data; /* Don't load the blank entry */ if (entry == blank_entry) continue; /* If this is the first load of the ledger, fill the quickfill cells */ { /* XXX */ } if (entry == find_entry) new_entry_row = vcell_loc.virt_row; gnc_table_set_vcell (table, cursor, gncEntryGetGUID (entry), TRUE, start_primary_color, vcell_loc); vcell_loc.virt_row++; /* Flip color for the next guy */ start_primary_color = !start_primary_color; } /* Add the blank entry at the end. */ if (blank_entry) { gnc_table_set_vcell (table, cursor, gncEntryGetGUID (blank_entry), TRUE, start_primary_color, vcell_loc); if (find_entry == blank_entry) new_entry_row = vcell_loc.virt_row; vcell_loc.virt_row++; } /* Resize the table */ gnc_table_set_size (table, vcell_loc.virt_row, 1); /* Restore the cursor to its rightful position */ if (new_entry_row > 0) save_loc.vcell_loc.virt_row = new_entry_row; if (gnc_table_find_close_valid_cell (table, &save_loc, FALSE)) { gnc_table_move_cursor_gui (table, save_loc); if (find_entry == gnc_entry_ledger_get_current_entry (ledger)) gnc_table_restore_current_cursor (table, cursor_buffer); } gnc_cursor_buffer_destroy (cursor_buffer); cursor_buffer = NULL; /* Reset the ledger */ ledger->traverse_to_new = FALSE; ledger->hint_entry = NULL; /* Set the cell fractions */ gnc_table_refresh_gui (table, TRUE); gnc_entry_ledger_show_entry (ledger, table->current_cursor_loc.vcell_loc); /* Set completion character */ gnc_combo_cell_set_complete_char ((ComboCell *) gnc_table_layout_get_cell (table->layout, ENTRY_IACCT_CELL), gnc_get_account_separator ()); gnc_combo_cell_set_complete_char ((ComboCell *) gnc_table_layout_get_cell (table->layout, ENTRY_BACCT_CELL), gnc_get_account_separator ()); /* enable callback for cursor user-driven moves */ gnc_table_control_allow_move (table->control, TRUE); }
void gnc_split_register_load (SplitRegister *reg, GList * slist, Account *default_account) { SRInfo *info; Transaction *pending_trans; CursorBuffer *cursor_buffer; GHashTable *trans_table = NULL; CellBlock *cursor_header; CellBlock *lead_cursor; CellBlock *split_cursor; Transaction *blank_trans; Transaction *find_trans; Transaction *trans; CursorClass find_class; Split *find_trans_split; Split *blank_split; Split *find_split; Split *split; Table *table; GList *node; gboolean start_primary_color = TRUE; gboolean found_pending = FALSE; gboolean need_divider_upper = FALSE; gboolean found_divider_upper = FALSE; gboolean found_divider = FALSE; gboolean has_last_num = FALSE; gboolean multi_line; gboolean dynamic; gboolean we_own_slist = FALSE; gboolean use_autoreadonly = qof_book_uses_autoreadonly(gnc_get_current_book()); VirtualCellLocation vcell_loc; VirtualLocation save_loc; int new_trans_split_row = -1; int new_trans_row = -1; int new_split_row = -1; time64 present, autoreadonly_time = 0; g_return_if_fail(reg); table = reg->table; g_return_if_fail(table); info = gnc_split_register_get_info (reg); g_return_if_fail(info); ENTER("reg=%p, slist=%p, default_account=%p", reg, slist, default_account); blank_split = xaccSplitLookup (&info->blank_split_guid, gnc_get_current_book ()); pending_trans = xaccTransLookup (&info->pending_trans_guid, gnc_get_current_book ()); /* make sure we have a blank split */ if (blank_split == NULL) { /* Wouldn't it be a bug to open the new transaction if there was * already a pending transaction? */ g_assert(pending_trans == NULL); blank_split = create_blank_split (default_account, info); } blank_trans = xaccSplitGetParent (blank_split); DEBUG("blank_split=%p, blank_trans=%p, pending_trans=%p", blank_split, blank_trans, pending_trans); info->default_account = *xaccAccountGetGUID (default_account); // gnc_table_leave_update (table, table->current_cursor_loc); multi_line = (reg->style == REG_STYLE_JOURNAL); dynamic = (reg->style == REG_STYLE_AUTO_LEDGER); lead_cursor = gnc_split_register_get_passive_cursor (reg); split_cursor = gnc_table_layout_get_cursor (table->layout, CURSOR_SPLIT); /* figure out where we are going to. */ if (info->traverse_to_new) { find_trans = blank_trans; find_split = NULL; find_trans_split = blank_split; find_class = CURSOR_CLASS_SPLIT; } else { find_trans = info->cursor_hint_trans; find_split = info->cursor_hint_split; find_trans_split = info->cursor_hint_trans_split; find_class = info->cursor_hint_cursor_class; } save_loc = table->current_cursor_loc; /* If the current cursor has changed we save the values for later * possible restoration. */ if (gnc_table_current_cursor_changed (table, TRUE) && (find_split == gnc_split_register_get_current_split (reg))) { cursor_buffer = gnc_cursor_buffer_new (); gnc_table_save_current_cursor (table, cursor_buffer); } else cursor_buffer = NULL; /* disable move callback -- we don't want the cascade of * callbacks while we are fiddling with loading the register */ gnc_table_control_allow_move (table->control, FALSE); /* invalidate the cursor */ { VirtualLocation virt_loc; gnc_virtual_location_init(&virt_loc); gnc_table_move_cursor_gui (table, virt_loc); } /* make sure that the header is loaded */ vcell_loc.virt_row = 0; vcell_loc.virt_col = 0; cursor_header = gnc_table_layout_get_cursor (table->layout, CURSOR_HEADER); gnc_table_set_vcell (table, cursor_header, NULL, TRUE, TRUE, vcell_loc); vcell_loc.virt_row++; /* get the current time and reset the dividing row */ present = gnc_time64_get_today_end (); if (use_autoreadonly) { GDate *d = qof_book_get_autoreadonly_gdate(gnc_get_current_book()); // "d" is NULL if use_autoreadonly is FALSE autoreadonly_time = d ? timespecToTime64(gdate_to_timespec(*d)) : 0; g_date_free(d); } if (info->first_pass) { if (default_account) { const char *last_num = xaccAccountGetLastNum (default_account); if (last_num) { NumCell *cell; cell = (NumCell *) gnc_table_layout_get_cell(table->layout, NUM_CELL); gnc_num_cell_set_last_num (cell, last_num); has_last_num = TRUE; } } /* load up account names into the transfer combobox menus */ gnc_split_register_load_xfer_cells (reg, default_account); gnc_split_register_load_recn_cells (reg); gnc_split_register_load_type_cells (reg); } if (info->separator_changed) change_account_separator (info, table, reg); table->model->dividing_row_upper = -1; table->model->dividing_row = -1; // Ensure that the transaction and splits being edited are in the split // list we're about to load. if (pending_trans != NULL) { for (node = xaccTransGetSplitList(pending_trans); node; node = node->next) { Split *pending_split = (Split*)node->data; if (!xaccTransStillHasSplit(pending_trans, pending_split)) continue; if (g_list_find(slist, pending_split) != NULL) continue; if (g_list_find_custom(slist, pending_trans, _find_split_with_parent_txn) != NULL) continue; if (!we_own_slist) { // lazy-copy slist = g_list_copy(slist); we_own_slist = TRUE; } slist = g_list_append(slist, pending_split); } } if (multi_line) trans_table = g_hash_table_new (g_direct_hash, g_direct_equal); /* populate the table */ for (node = slist; node; node = node->next) { split = node->data; trans = xaccSplitGetParent (split); if (!xaccTransStillHasSplit(trans, split)) continue; if (pending_trans == trans) found_pending = TRUE; /* If the transaction has only one split, and it's not our * pending_trans, then it's another register's blank split and * we don't want to see it. */ else if (xaccTransCountSplits (trans) == 1 && xaccSplitGetAccount (split) == NULL) continue; /* Do not load splits from the blank transaction. */ if (trans == blank_trans) continue; if (multi_line) { /* Skip this split if its transaction has already been loaded. */ if (g_hash_table_lookup (trans_table, trans)) continue; g_hash_table_insert (trans_table, trans, trans); } if (info->show_present_divider && use_autoreadonly && !found_divider_upper) { if (xaccTransGetDate (trans) >= autoreadonly_time) { table->model->dividing_row_upper = vcell_loc.virt_row; found_divider_upper = TRUE; } else { need_divider_upper = TRUE; } } if (info->show_present_divider && !found_divider && (xaccTransGetDate (trans) > present)) { table->model->dividing_row = vcell_loc.virt_row; found_divider = TRUE; } /* If this is the first load of the register, * fill up the quickfill cells. */ if (info->first_pass) add_quickfill_completions(reg->table->layout, trans, split, has_last_num); if (trans == find_trans) new_trans_row = vcell_loc.virt_row; if (split == find_trans_split) new_trans_split_row = vcell_loc.virt_row; gnc_split_register_add_transaction (reg, trans, split, lead_cursor, split_cursor, multi_line, start_primary_color, TRUE, find_trans, find_split, find_class, &new_split_row, &vcell_loc); if (!multi_line) start_primary_color = !start_primary_color; } if (multi_line) g_hash_table_destroy (trans_table); /* add the blank split at the end. */ if (pending_trans == blank_trans) found_pending = TRUE; /* No upper divider yet? Store it now */ if (info->show_present_divider && use_autoreadonly && !found_divider_upper && need_divider_upper) { table->model->dividing_row_upper = vcell_loc.virt_row; found_divider_upper = TRUE; } if (blank_trans == find_trans) new_trans_row = vcell_loc.virt_row; if (blank_split == find_trans_split) new_trans_split_row = vcell_loc.virt_row; /* If we didn't find the pending transaction, it was removed * from the account. */ if (!found_pending) { info->pending_trans_guid = *guid_null (); if (xaccTransIsOpen (pending_trans)) xaccTransCommitEdit (pending_trans); else if (pending_trans) g_assert_not_reached(); pending_trans = NULL; } /* go to blank on first pass */ if (info->first_pass) { new_split_row = -1; new_trans_split_row = -1; new_trans_row = -1; save_loc.vcell_loc = vcell_loc; save_loc.phys_row_offset = 0; save_loc.phys_col_offset = 0; } gnc_split_register_add_transaction (reg, blank_trans, blank_split, lead_cursor, split_cursor, multi_line, start_primary_color, info->blank_split_edited, find_trans, find_split, find_class, &new_split_row, &vcell_loc); /* resize the table to the sizes we just counted above */ /* num_virt_cols is always one. */ gnc_table_set_size (table, vcell_loc.virt_row, 1); /* restore the cursor to its rightful position */ { VirtualLocation trans_split_loc; if (new_split_row > 0) save_loc.vcell_loc.virt_row = new_split_row; else if (new_trans_split_row > 0) save_loc.vcell_loc.virt_row = new_trans_split_row; else if (new_trans_row > 0) save_loc.vcell_loc.virt_row = new_trans_row; trans_split_loc = save_loc; gnc_split_register_get_trans_split (reg, save_loc.vcell_loc, &trans_split_loc.vcell_loc); if (dynamic || multi_line || info->trans_expanded) { gnc_table_set_virt_cell_cursor( table, trans_split_loc.vcell_loc, gnc_split_register_get_active_cursor (reg)); gnc_split_register_set_trans_visible (reg, trans_split_loc.vcell_loc, TRUE, multi_line); info->trans_expanded = (reg->style == REG_STYLE_LEDGER); } else { save_loc = trans_split_loc; info->trans_expanded = FALSE; } if (gnc_table_find_close_valid_cell (table, &save_loc, FALSE)) { gnc_table_move_cursor_gui (table, save_loc); new_split_row = save_loc.vcell_loc.virt_row; if (find_split == gnc_split_register_get_current_split (reg)) gnc_table_restore_current_cursor (table, cursor_buffer); } } gnc_cursor_buffer_destroy (cursor_buffer); cursor_buffer = NULL; update_info (info, reg); gnc_split_register_set_cell_fractions( reg, gnc_split_register_get_current_split (reg)); gnc_table_refresh_gui (table, TRUE); gnc_split_register_show_trans (reg, table->current_cursor_loc.vcell_loc); /* enable callback for cursor user-driven moves */ gnc_table_control_allow_move (table->control, TRUE); if (we_own_slist) g_list_free(slist); LEAVE(" "); }