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(" "); }
/* Either sets the value and amount for split and returns TRUE, or does nothing and returns FALSE. */ static gboolean gtu_sr_handle_exchange_rate (GncTreeViewSplitReg *view, gnc_numeric amount, Transaction *trans, Split *split, gboolean force) { GncTreeModelSplitReg *model; XferDialog *xfer; gboolean rate_split_ok, rate_reg_ok; gnc_numeric rate_split, rate_reg, value; Account *reg_acc; gnc_commodity *xfer_comm = xaccAccountGetCommodity (xaccSplitGetAccount (split)); gnc_commodity *reg_comm = gnc_tree_view_split_reg_get_reg_commodity (view); gnc_commodity *trans_curr = xaccTransGetCurrency (trans); gboolean expanded; gboolean have_rate = TRUE; ENTER("handle_exchange_rate amount %s, trans %p and split %p force %d", gnc_numeric_to_string (amount), trans, split, force); model = gnc_tree_view_split_reg_get_model_from_view (view); reg_acc = gnc_tree_model_split_reg_get_anchor (model); /* Rate from trans-curr to split-comm */ rate_split_ok = xaccTransGetRateForCommodity (trans, xfer_comm, split, &rate_split); DEBUG("rate_split_ok %d and xfer_comm %s", rate_split_ok, gnc_commodity_get_fullname (xfer_comm)); /* Rate from trans-curr to reg-comm */ rate_reg_ok = xaccTransGetRateForCommodity (trans, reg_comm, split, &rate_reg); DEBUG("rate_reg_ok %d and reg_comm %s", rate_reg_ok, gnc_commodity_get_fullname (reg_comm)); /* Are we expanded */ expanded = gnc_tree_view_split_reg_trans_expanded (view, trans); if (gnc_commodity_equal (trans_curr, xfer_comm) && rate_split_ok) { xaccSplitSetAmount (split, amount); xaccSplitSetValue (split, amount); return TRUE; } if (rate_reg_ok && rate_split_ok && !force) { value = gnc_numeric_div (amount, rate_reg, gnc_commodity_get_fraction (trans_curr), GNC_HOW_DENOM_REDUCE); amount = gnc_numeric_mul (value, rate_split, GNC_DENOM_AUTO, GNC_HOW_RND_ROUND); } else { if (!rate_split_ok) rate_split = gtu_sr_get_rate_from_db (reg_comm, xfer_comm); /* create the exchange-rate dialog */ xfer = gnc_xfer_dialog (NULL, NULL); gnc_xfer_dialog_is_exchange_dialog (xfer, &rate_split); /* fill in the dialog entries */ gnc_xfer_dialog_set_description (xfer, xaccTransGetDescription (trans)); gnc_xfer_dialog_set_memo (xfer, xaccSplitGetMemo (split)); /* Get per book option */ gnc_xfer_dialog_set_num (xfer, gnc_get_num_action (trans, split)); gnc_xfer_dialog_set_date (xfer, timespecToTime64 (xaccTransRetDatePostedTS (trans))); value = amount; if (gnc_xfer_dialog_run_exchange_dialog (xfer, &rate_split, value, reg_acc, trans, xfer_comm, expanded)) { if (!rate_split_ok) rate_split = gnc_numeric_create (1, 1); have_rate = FALSE; } else have_rate = TRUE; amount = gnc_numeric_mul (value, rate_split, GNC_DENOM_AUTO, GNC_HOW_RND_ROUND); } xaccSplitSetAmount (split, amount); xaccSplitSetValue (split, value); LEAVE("handle_exchange_rate set split %p amt=%s; and val=%s", split, gnc_numeric_to_string (amount), gnc_numeric_to_string (value)); return have_rate; }
/** * @fixme Move this non-GUI code into the engine. **/ static void gnc_ui_accounts_recurse (Account *parent, GList **currency_list, GNCSummarybarOptions options) { gnc_numeric start_amount; gnc_numeric start_amount_default_currency; gnc_numeric end_amount; gnc_numeric end_amount_default_currency; GNCAccountType account_type; gnc_commodity * account_currency; GNCCurrencyAcc *currency_accum = NULL; GNCCurrencyAcc *grand_total_accum = NULL; GNCCurrencyAcc *non_curr_accum = NULL; GList *children, *node; gboolean non_currency = FALSE; Timespec end_timespec; Timespec start_timespec; if (parent == NULL) return; children = gnc_account_get_children(parent); for (node = children; node; node = g_list_next(node)) { Account *account = node->data; account_type = xaccAccountGetType(account); account_currency = xaccAccountGetCommodity(account); if (options.grand_total) grand_total_accum = gnc_ui_get_currency_accumulator(currency_list, options.default_currency, TOTAL_GRAND_TOTAL); if (!gnc_commodity_is_currency(account_currency)) { non_currency = TRUE; non_curr_accum = gnc_ui_get_currency_accumulator(currency_list, options.default_currency, TOTAL_NON_CURR_TOTAL); } if (!non_currency || options.non_currency) { currency_accum = gnc_ui_get_currency_accumulator(currency_list, account_currency, TOTAL_SINGLE); } switch (account_type) { case ACCT_TYPE_BANK: case ACCT_TYPE_CASH: case ACCT_TYPE_ASSET: case ACCT_TYPE_STOCK: case ACCT_TYPE_MUTUAL: case ACCT_TYPE_CREDIT: case ACCT_TYPE_LIABILITY: case ACCT_TYPE_PAYABLE: case ACCT_TYPE_RECEIVABLE: end_amount = xaccAccountGetBalanceAsOfDate(account, options.end_date); timespecFromTime64(&end_timespec, options.end_date); end_amount_default_currency = xaccAccountConvertBalanceToCurrencyAsOfDate (account, end_amount, account_currency, options.default_currency, timespecToTime64(timespecCanonicalDayTime(end_timespec))); if (!non_currency || options.non_currency) { currency_accum->assets = gnc_numeric_add (currency_accum->assets, end_amount, gnc_commodity_get_fraction (account_currency), GNC_HOW_RND_ROUND_HALF_UP); } if (non_currency) { non_curr_accum->assets = gnc_numeric_add (non_curr_accum->assets, end_amount_default_currency, gnc_commodity_get_fraction (options.default_currency), GNC_HOW_RND_ROUND_HALF_UP); } if (options.grand_total) { grand_total_accum->assets = gnc_numeric_add (grand_total_accum->assets, end_amount_default_currency, gnc_commodity_get_fraction (options.default_currency), GNC_HOW_RND_ROUND_HALF_UP); } gnc_ui_accounts_recurse(account, currency_list, options); break; case ACCT_TYPE_INCOME: case ACCT_TYPE_EXPENSE: start_amount = xaccAccountGetBalanceAsOfDate(account, options.start_date); timespecFromTime64(&start_timespec, options.start_date); start_amount_default_currency = xaccAccountConvertBalanceToCurrencyAsOfDate (account, start_amount, account_currency, options.default_currency, timespecToTime64(timespecCanonicalDayTime(start_timespec))); end_amount = xaccAccountGetBalanceAsOfDate(account, options.end_date); timespecFromTime64(&end_timespec, options.end_date); end_amount_default_currency = xaccAccountConvertBalanceToCurrencyAsOfDate (account, end_amount, account_currency, options.default_currency, timespecToTime64(timespecCanonicalDayTime(end_timespec))); if (!non_currency || options.non_currency) { currency_accum->profits = gnc_numeric_add (currency_accum->profits, start_amount, gnc_commodity_get_fraction (account_currency), GNC_HOW_RND_ROUND_HALF_UP); currency_accum->profits = gnc_numeric_sub (currency_accum->profits, end_amount, gnc_commodity_get_fraction (account_currency), GNC_HOW_RND_ROUND_HALF_UP); } if (non_currency) { non_curr_accum->profits = gnc_numeric_add (non_curr_accum->profits, start_amount_default_currency, gnc_commodity_get_fraction (options.default_currency), GNC_HOW_RND_ROUND_HALF_UP); non_curr_accum->profits = gnc_numeric_sub (non_curr_accum->profits, end_amount_default_currency, gnc_commodity_get_fraction (options.default_currency), GNC_HOW_RND_ROUND_HALF_UP); } if (options.grand_total) { grand_total_accum->profits = gnc_numeric_add (grand_total_accum->profits, start_amount_default_currency, gnc_commodity_get_fraction (options.default_currency), GNC_HOW_RND_ROUND_HALF_UP); grand_total_accum->profits = gnc_numeric_sub (grand_total_accum->profits, end_amount_default_currency, gnc_commodity_get_fraction (options.default_currency), GNC_HOW_RND_ROUND_HALF_UP); } gnc_ui_accounts_recurse(account, currency_list, options); break; case ACCT_TYPE_EQUITY: /* no-op, see comments at top about summing assets */ break; /** * @fixme I don't know if this is right or if trading accounts should be * treated like income and expense accounts. **/ case ACCT_TYPE_TRADING: break; case ACCT_TYPE_CURRENCY: default: break; } } g_list_free(children); }
void gnc_payment_window_fill_docs_list (PaymentWindow *pw) { GtkListStore *store; GList *list = NULL, *node; g_return_if_fail (pw->docs_list_tree_view && GTK_IS_TREE_VIEW(pw->docs_list_tree_view)); /* Get a list of open lots for this owner and post account */ if (pw->owner.owner.undefined) list = xaccAccountFindOpenLots (pw->post_acct, gncOwnerLotMatchOwnerFunc, &pw->owner, NULL); /* Clear the existing list */ store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(pw->docs_list_tree_view))); gtk_list_store_clear(store); /* Add the documents and overpayments to the tree view */ for (node = list; node; node = node->next) { GNCLot *lot = node->data; time64 doc_date_time = 0; const gchar *doc_type_str = NULL; const gchar *doc_id_str = NULL; const gchar *doc_deb_str = NULL; const gchar *doc_cred_str = NULL; GtkTreeIter iter; Timespec doc_date; GncInvoice *document; gnc_numeric value = gnc_numeric_zero(); gnc_numeric debit = gnc_numeric_zero(); gnc_numeric credit = gnc_numeric_zero(); /* Find the lot's document if it exists, * it could also be a prepayment lot. */ document = gncInvoiceGetInvoiceFromLot (lot); /* Find the document's date or pre-payment date */ if (document) doc_date = gncInvoiceGetDatePosted (document); else { /* Calculate the payment date based on the lot splits */ Transaction *trans = xaccSplitGetParent (gnc_lot_get_latest_split (lot)); if (trans) doc_date = xaccTransRetDatePostedTS (trans); else continue; /* No valid split in this lot, skip it */ } doc_date_time = timespecToTime64 (doc_date); /* Find the document type. No type means pre-payment in this case */ if (document) { doc_type_str = gncInvoiceGetTypeString (document); } else doc_type_str = _("Pre-Payment"); /* Find the document id. Empty for pre-payments. */ if (document) { doc_id_str = gncInvoiceGetID (document); } /* Find the debit/credit amount. * Invoices/vendor credit notes are debit (increasing the balance) * Customer credit notes/bills are credit (decreasing the balance) * Pre-payments are debit or credit depending on their sign */ value = gnc_lot_get_balance (lot); if (gnc_numeric_positive_p (value)) debit = value; else credit = gnc_numeric_neg (value); /* Only display non-zero debits/credits */ if (!gnc_numeric_zero_p (debit)) doc_deb_str = xaccPrintAmount (debit, gnc_default_print_info (FALSE)); if (!gnc_numeric_zero_p (credit)) doc_cred_str = xaccPrintAmount (credit, gnc_default_print_info (FALSE)); gtk_list_store_append (store, &iter); gtk_list_store_set (store, &iter, 0, doc_date_time, 1, doc_id_str, 2, doc_type_str, 3, doc_deb_str, 4, doc_cred_str, 5, (gpointer)lot, -1); } g_list_free (list); /* Highlight the preset invoice if it's in the new list */ gnc_payment_dialog_highlight_document (pw); }
static void gncOwnerCreateLotLink (GNCLot *from_lot, GNCLot *to_lot, const GncOwner *owner) { const gchar *action = _("Lot Link"); Account *acct = gnc_lot_get_account (from_lot); const gchar *name = gncOwnerGetName (gncOwnerGetEndOwner (owner)); Transaction *ll_txn = NULL; gnc_numeric from_lot_bal, to_lot_bal; Timespec from_ts, to_ts; time64 time_posted; Split *split; /* Sanity check */ if (!gncInvoiceGetInvoiceFromLot (from_lot) || !gncInvoiceGetInvoiceFromLot (to_lot)) return; /* Determine transaction date based on lot splits */ from_ts = xaccTransRetDatePostedTS (xaccSplitGetParent (gnc_lot_get_latest_split (from_lot))); to_ts = xaccTransRetDatePostedTS (xaccSplitGetParent (gnc_lot_get_latest_split (to_lot))); if (timespecToTime64 (from_ts) >= timespecToTime64 (to_ts)) time_posted = timespecToTime64 (from_ts); else time_posted = timespecToTime64 (to_ts); /* Figure out how much we can offset between the lots */ from_lot_bal = gnc_lot_get_balance (from_lot); to_lot_bal = gnc_lot_get_balance (to_lot); if (gnc_numeric_compare (gnc_numeric_abs (from_lot_bal), gnc_numeric_abs (to_lot_bal)) > 0) from_lot_bal = gnc_numeric_neg (to_lot_bal); else to_lot_bal = gnc_numeric_neg (from_lot_bal); xaccAccountBeginEdit (acct); /* Look for a pre-existing lot link we can extend */ ll_txn = get_ll_transaction_from_lot (from_lot); if (!ll_txn) ll_txn = get_ll_transaction_from_lot (to_lot); if (!ll_txn) { /* No pre-existing lot link. Create one. */ Timespec ts; timespecFromTime64 (&ts, time_posted); ll_txn = xaccMallocTransaction (gnc_lot_get_book (from_lot)); xaccTransBeginEdit (ll_txn); xaccTransSetDescription (ll_txn, name ? name : "(Unknown)"); xaccTransSetCurrency (ll_txn, xaccAccountGetCommodity(acct)); xaccTransSetDateEnteredSecs (ll_txn, gnc_time (NULL)); xaccTransSetDatePostedTS (ll_txn, &ts); xaccTransSetTxnType (ll_txn, TXN_TYPE_LINK); } else { Timespec ts = xaccTransRetDatePostedTS (ll_txn); xaccTransBeginEdit (ll_txn); /* Maybe we need to update the post date of the transaction ? */ if (time_posted > timespecToTime64 (ts)) { timespecFromTime64 (&ts, time_posted); xaccTransSetDatePostedTS (ll_txn, &ts); } } /* Create a split for the from_lot */ split = xaccMallocSplit (gnc_lot_get_book (from_lot)); /* set Action using utility function */ gnc_set_num_action (NULL, split, NULL, action); xaccAccountInsertSplit (acct, split); xaccTransAppendSplit (ll_txn, split); /* To offset the lot balance, the split must be of the opposite sign */ xaccSplitSetBaseValue (split, gnc_numeric_neg (from_lot_bal), xaccAccountGetCommodity(acct)); gnc_lot_add_split (from_lot, split); /* Create a split for the to_lot */ split = xaccMallocSplit (gnc_lot_get_book (to_lot)); /* set Action using utility function */ gnc_set_num_action (NULL, split, NULL, action); xaccAccountInsertSplit (acct, split); xaccTransAppendSplit (ll_txn, split); /* To offset the lot balance, the split must be of the opposite sign */ xaccSplitSetBaseValue (split, gnc_numeric_neg (to_lot_bal), xaccAccountGetCommodity(acct)); gnc_lot_add_split (to_lot, split); xaccTransCommitEdit (ll_txn); /* Do some post-cleaning on the lots * The above actions may have created splits that are * in the same transaction and lot. These can be merged. */ xaccScrubMergeLotSubSplits (to_lot, FALSE); xaccScrubMergeLotSubSplits (from_lot, FALSE); /* And finally set the same memo for all remaining splits * It's a convenience for the users to identify all documents * involved in the link. */ gncOwnerSetLotLinkMemo (ll_txn); xaccAccountCommitEdit (acct); }