void xaccAccountAssignLots (Account *acc) { SplitList *splits, *node; if (!acc) return; ENTER ("acc=%s", xaccAccountGetName(acc)); xaccAccountBeginEdit (acc); restart_loop: splits = xaccAccountGetSplitList(acc); for (node = splits; node; node = node->next) { Split * split = node->data; /* If already in lot, then no-op */ if (split->lot) continue; /* Skip voided transactions */ if (gnc_numeric_zero_p (split->amount) && xaccTransGetVoidStatus(split->parent)) continue; if (xaccSplitAssign (split)) goto restart_loop; } xaccAccountCommitEdit (acc); LEAVE ("acc=%s", xaccAccountGetName(acc)); }
static void teardown( Fixture *fixture, gconstpointer pData ) { xaccAccountBeginEdit(fixture->account); xaccAccountDestroy(fixture->account); gnc_commodity_destroy(fixture->commodity); qof_book_destroy( fixture->book ); }
static void delete_our_account_tree (hierarchy_data *data) { if (data->our_account_tree != NULL) { xaccAccountBeginEdit (data->our_account_tree); xaccAccountDestroy (data->our_account_tree); data->our_account_tree = NULL; } }
gboolean gncScrubBusinessLot (GNCLot *lot) { gboolean splits_deleted = FALSE; gboolean dangling_payments = FALSE; gboolean dangling_lot_link = FALSE; Account *acc; gchar *lotname=NULL; if (!lot) return FALSE; lotname = g_strdup (gnc_lot_get_title (lot)); ENTER ("(lot=%p) %s", lot, lotname ? lotname : "(no lotname)"); acc = gnc_lot_get_account (lot); if (acc) xaccAccountBeginEdit(acc); // Scrub lot links. // They should only remain when two document lots are linked together xaccScrubMergeLotSubSplits (lot, FALSE); splits_deleted = gncScrubLotLinks (lot); // Look for dangling payments and repair if found dangling_lot_link = gncScrubLotIsSingleLotLinkSplit (lot); if (dangling_lot_link) { dangling_payments = gncScrubLotDanglingPayments (lot); if (dangling_payments) splits_deleted |= gncScrubLotLinks (lot); else { Split *split = gnc_lot_get_earliest_split (lot); Transaction *trans = xaccSplitGetParent (split); xaccTransDestroy (trans); } } // If lot is empty now, delete it if (0 == gnc_lot_count_splits (lot)) { PINFO("All splits were removed from lot, deleting"); gnc_lot_destroy (lot); } if (acc) xaccAccountCommitEdit(acc); LEAVE ("(lot=%s, deleted=%d, dangling lot link=%d, dangling_payments=%d)", lotname ? lotname : "(no lotname)", splits_deleted, dangling_lot_link, dangling_payments); g_free (lotname); return splits_deleted; }
/* Used in the midst of editing a transaction; make it save the * account data. */ void gnc_import_set_acc_online_id(Account * account, const gchar * string_value) { kvp_frame * frame; g_return_if_fail (account != NULL); frame = xaccAccountGetSlots(account); xaccAccountBeginEdit (account); kvp_frame_set_str(frame, "online_id", string_value); qof_instance_set_dirty (QOF_INSTANCE (account)); xaccAccountCommitEdit (account); }
static void xaccAccountDeleteOldData (Account *account) { if (!account) return; xaccAccountBeginEdit (account); qof_instance_set_kvp (QOF_INSTANCE (account), "old-currency", NULL); qof_instance_set_kvp (QOF_INSTANCE (account), "old-security", NULL); qof_instance_set_kvp (QOF_INSTANCE (account), "old-currency-scu", NULL); qof_instance_set_kvp (QOF_INSTANCE (account), "old-security-scu", NULL); qof_instance_set_dirty (QOF_INSTANCE (account)); xaccAccountCommitEdit (account); }
static void sxtg_book_begin (QofBook *book) { Account *root; root = xaccMallocAccount(book); xaccAccountBeginEdit(root); xaccAccountSetType(root, ACCT_TYPE_ROOT); xaccAccountSetName(root, "Template Root"); xaccAccountCommitEdit(root); gnc_book_set_template_root (book, root); }
void sx_set_template_account (SchedXaction *sx, Account *account) { Account *old; old = sx->template_acct; sx->template_acct = account; if (old) { xaccAccountBeginEdit(old); xaccAccountDestroy(old); } }
void xaccLotFill (GNCLot *lot) { Account *acc; Split *split; GNCPolicy *pcy; if (!lot) return; acc = gnc_lot_get_account(lot); pcy = gnc_account_get_policy(acc); ENTER ("(lot=%s, acc=%s)", gnc_lot_get_title(lot), xaccAccountGetName(acc)); /* If balance already zero, we have nothing to do. */ if (gnc_lot_is_closed (lot)) return; split = pcy->PolicyGetSplit (pcy, lot); if (!split) return; /* Handle the common case */ /* Reject voided transactions */ if (gnc_numeric_zero_p(split->amount) && xaccTransGetVoidStatus(split->parent)) return; xaccAccountBeginEdit (acc); /* Loop until we've filled up the lot, (i.e. till the * balance goes to zero) or there are no splits left. */ while (1) { Split *subsplit; subsplit = xaccSplitAssignToLot (split, lot); if (subsplit == split) { PERR ("Accounting Policy gave us a split that " "doesn't fit into this lot\n" "lot baln=%s, isclosed=%d, aplit amt=%s", gnc_num_dbg_to_string (gnc_lot_get_balance(lot)), gnc_lot_is_closed (lot), gnc_num_dbg_to_string (split->amount)); break; } if (gnc_lot_is_closed (lot)) break; split = pcy->PolicyGetSplit (pcy, lot); if (!split) break; } xaccAccountCommitEdit (acc); LEAVE ("(lot=%s, acc=%s)", gnc_lot_get_title(lot), xaccAccountGetName(acc)); }
static gboolean tt_act_handler( xmlNodePtr node, gpointer data ) { gnc_template_xaction_data *txd = data; Account *acc; gnc_commodity *com; acc = dom_tree_to_account(node, txd->book); if ( acc == NULL ) { return FALSE; } else { xaccAccountBeginEdit (acc); /* Check for the lack of a commodity [signifying that the pre-7/11/2001-CIT-change SX template Account was parsed [but incorrectly]. */ if ( xaccAccountGetCommodity( acc ) == NULL ) { #if 1 gnc_commodity_table* table; table = gnc_commodity_table_get_table( txd->book ); com = gnc_commodity_table_lookup( table, "template", "template" ); #else /* FIXME: This should first look in the table of the book, maybe? The right thing happens [WRT file load/save] if we just _new all the time, but it doesn't seem right. This whole block should go away at some point, but the same concern still applies for SchedXaction.c:xaccSchedXactionInit... */ com = gnc_commodity_new( txd->book, "template", "template", "template", "template", 1 ); #endif xaccAccountSetCommodity( acc, com ); } txd->accts = g_list_append( txd->accts, acc ); } return TRUE; }
static void xaccSchedXactionFree( SchedXaction *sx ) { GList *l; if ( sx == NULL ) return; qof_event_gen( &sx->inst, QOF_EVENT_DESTROY , NULL); if ( sx->name ) g_free( sx->name ); /* * we have to delete the transactions in the * template account ourselves */ delete_template_trans( sx ); /* * xaccAccountDestroy removes the account from * its group for us AFAICT. If shutting down, * the account is being deleted separately. */ if (!qof_book_shutting_down(qof_instance_get_book(sx))) { xaccAccountBeginEdit(sx->template_acct); xaccAccountDestroy(sx->template_acct); } for ( l = sx->deferredList; l; l = l->next ) { gnc_sx_destroy_temporal_state( l->data ); l->data = NULL; } if ( sx->deferredList ) { g_list_free( sx->deferredList ); sx->deferredList = NULL; } /* qof_instance_release (&sx->inst); */ g_object_unref( sx ); }
static void merge_splits (Split *sa, Split *sb) { Account *act; Transaction *txn; gnc_numeric amt, val; act = xaccSplitGetAccount (sb); xaccAccountBeginEdit (act); txn = sa->parent; xaccTransBeginEdit (txn); /* Remove the guid of sb from the 'gemini' of sa */ remove_guids (sa, sb); /* Add amount of sb into sa, ditto for value. */ amt = xaccSplitGetAmount (sa); amt = gnc_numeric_add_fixed (amt, xaccSplitGetAmount (sb)); xaccSplitSetAmount (sa, amt); val = xaccSplitGetValue (sa); val = gnc_numeric_add_fixed (val, xaccSplitGetValue (sb)); xaccSplitSetValue (sa, val); /* Set reconcile to no; after this much violence, * no way its reconciled. */ xaccSplitSetReconcile (sa, NREC); /* If sb has associated gains splits, trash them. */ if ((sb->gains_split) && (sb->gains_split->gains & GAINS_STATUS_GAINS)) { Transaction *t = sb->gains_split->parent; xaccTransBeginEdit (t); xaccTransDestroy (t); xaccTransCommitEdit (t); } /* Finally, delete sb */ xaccSplitDestroy(sb); xaccTransCommitEdit (txn); xaccAccountCommitEdit (act); }
static void gnc_collection_set_template_root (QofCollection *col, Account *templateRoot) { Account *old_root; if (!col) return; old_root = gnc_collection_get_template_root (col); if (old_root == templateRoot) return; qof_collection_set_data (col, templateRoot); if (old_root) { xaccAccountBeginEdit (old_root); xaccAccountDestroy (old_root); } }
static Transaction* gsr2_create_balancing_transaction (QofBook *book, Account *account, time64 statement_date, gnc_numeric balancing_amount) { Transaction *trans; Split *split; if (!account) return NULL; if (gnc_numeric_zero_p (balancing_amount)) return NULL; xaccAccountBeginEdit (account); trans = xaccMallocTransaction (book); xaccTransBeginEdit (trans); // fill Transaction xaccTransSetCurrency (trans, gnc_account_or_default_currency (account, NULL)); xaccTransSetDatePostedSecsNormalized (trans, statement_date); xaccTransSetDescription (trans, _("Balancing entry from reconciliation")); // 1. Split split = xaccMallocSplit (book); xaccTransAppendSplit (trans, split); xaccAccountInsertSplit (account, split); xaccSplitSetAmount (split, balancing_amount); xaccSplitSetValue (split, balancing_amount); // 2. Split (no account is defined: split goes to orphan account) split = xaccMallocSplit (book); xaccTransAppendSplit (trans, split); balancing_amount = gnc_numeric_neg (balancing_amount); xaccSplitSetAmount (split, balancing_amount); xaccSplitSetValue (split, balancing_amount); xaccTransCommitEdit (trans); xaccAccountCommitEdit (account); return trans; }
void gnc_ofx_kvp_set_assoc_account(Account* investment_account, const Account *income_account) { kvp_frame * acc_frame; kvp_value * kvp_val; const GncGUID * income_acc_guid; g_assert(investment_account); g_assert(income_account); acc_frame = xaccAccountGetSlots(investment_account); g_assert(acc_frame); // Must not be NULL, but the QofInstance doc is unclear about this income_acc_guid = xaccAccountGetGUID(income_account); kvp_val = kvp_value_new_guid(income_acc_guid); xaccAccountBeginEdit(investment_account); kvp_frame_set_slot_nc(acc_frame, KEY_ASSOC_INCOME_ACCOUNT, kvp_val); qof_instance_set_dirty(QOF_INSTANCE (investment_account)); xaccAccountCommitEdit(investment_account); }
static Account* load_single_account (GncSqlBackend* be, GncSqlRow* row, GList** l_accounts_needing_parents) { const GncGUID* guid; Account* pAccount = NULL; g_return_val_if_fail (be != NULL, NULL); g_return_val_if_fail (row != NULL, NULL); g_return_val_if_fail (l_accounts_needing_parents != NULL, NULL); guid = gnc_sql_load_guid (be, row); if (guid != NULL) { pAccount = xaccAccountLookup (guid, be->book); } if (pAccount == NULL) { pAccount = xaccMallocAccount (be->book); } xaccAccountBeginEdit (pAccount); gnc_sql_load_object (be, row, GNC_ID_ACCOUNT, pAccount, col_table); xaccAccountCommitEdit (pAccount); /* If we don't have a parent and this isn't the root account, it might be because the parent account hasn't been loaded yet. Remember the account and its parent guid for later. */ if (gnc_account_get_parent (pAccount) == NULL && pAccount != gnc_book_get_root_account (be->book)) { account_parent_guid_struct* s = static_cast<decltype (s)> ( g_malloc (sizeof (account_parent_guid_struct))); g_assert (s != NULL); s->pAccount = pAccount; gnc_sql_load_object (be, row, GNC_ID_ACCOUNT, s, parent_col_table); *l_accounts_needing_parents = g_list_prepend (*l_accounts_needing_parents, s); } return pAccount; }
static void set_root_template_guid( gpointer pObject, /*@ null @*/ gpointer pValue ) { QofBook* book = QOF_BOOK(pObject); GncGUID* guid = (GncGUID*)pValue; Account* root; g_return_if_fail( pObject != NULL ); g_return_if_fail( QOF_IS_BOOK(pObject) ); g_return_if_fail( pValue != NULL ); root = gnc_book_get_template_root( book ); if ( root == NULL ) { root = xaccMallocAccount( book ); xaccAccountBeginEdit( root ); xaccAccountSetType( root, ACCT_TYPE_ROOT ); xaccAccountCommitEdit( root ); gnc_book_set_template_root( book, root ); } qof_instance_set_guid( QOF_INSTANCE(root), guid ); }
void gncScrubBusinessAccountLots (Account *acc) { LotList *lots, *node; gint lot_count = 0; gint curr_lot_no = 1; const gchar *str; if (!acc) return; if (FALSE == xaccAccountIsAPARType (xaccAccountGetType (acc))) return; str = xaccAccountGetName(acc); str = str ? str : "(null)"; ENTER ("(acc=%s)", str); PINFO ("Cleaning up superfluous lot links in account %s \n", str); xaccAccountBeginEdit(acc); lots = xaccAccountGetLotList(acc); lot_count = g_list_length (lots); for (node = lots; node; node = node->next) { GNCLot *lot = node->data; PINFO("Start processing lot %d of %d", curr_lot_no, lot_count); if (lot) gncScrubBusinessLot (lot); PINFO("Finished processing lot %d of %d", curr_lot_no, lot_count); curr_lot_no++; } g_list_free(lots); xaccAccountCommitEdit(acc); LEAVE ("(acc=%s)", str); }
static Account* load_single_account (GncSqlBackend* sql_be, GncSqlRow& row, ParentGuidVec& l_accounts_needing_parents) { const GncGUID* guid; Account* pAccount = NULL; g_return_val_if_fail (sql_be != NULL, NULL); guid = gnc_sql_load_guid (sql_be, row); if (guid != NULL) { pAccount = xaccAccountLookup (guid, sql_be->book()); } if (pAccount == NULL) { pAccount = xaccMallocAccount (sql_be->book()); } xaccAccountBeginEdit (pAccount); gnc_sql_load_object (sql_be, row, GNC_ID_ACCOUNT, pAccount, col_table); xaccAccountCommitEdit (pAccount); /* If we don't have a parent and this isn't the root account, it might be because the parent account hasn't been loaded yet. Remember the account and its parent guid for later. */ if (gnc_account_get_parent (pAccount) == NULL && pAccount != gnc_book_get_root_account (sql_be->book())) { auto s = new ParentGuid; s->pAccount = pAccount; gnc_sql_load_object (sql_be, row, GNC_ID_ACCOUNT, s, parent_col_table); l_accounts_needing_parents.push_back(s); } return pAccount; }
Account * xaccScrubUtilityGetOrMakeAccount (Account *root, gnc_commodity * currency, const char *accname, GNCAccountType acctype, gboolean placeholder) { Account * acc; g_return_val_if_fail (root, NULL); /* build the account name */ if (!currency) { PERR ("No currency specified!"); return NULL; } /* See if we've got one of these going already ... */ acc = gnc_account_lookup_by_name(root, accname); if (acc == NULL) { /* Guess not. We'll have to build one. */ acc = xaccMallocAccount(gnc_account_get_book (root)); xaccAccountBeginEdit (acc); xaccAccountSetName (acc, accname); xaccAccountSetCommodity (acc, currency); xaccAccountSetType (acc, acctype); xaccAccountSetPlaceholder (acc, placeholder); /* Hang the account off the root. */ gnc_account_append_child (root, acc); xaccAccountCommitEdit (acc); } return acc; }
static void xaccSchedXactionInit(SchedXaction *sx, QofBook *book) { Account *ra; const GncGUID *guid; gchar guidstr[GUID_ENCODING_LENGTH+1]; qof_instance_init_data (&sx->inst, GNC_ID_SCHEDXACTION, book); /* create a new template account for our splits */ sx->template_acct = xaccMallocAccount(book); guid = qof_instance_get_guid( sx ); xaccAccountBeginEdit( sx->template_acct ); guid_to_string_buff( guid, guidstr ); xaccAccountSetName( sx->template_acct, guidstr); xaccAccountSetCommodity (sx->template_acct, gnc_commodity_table_lookup( gnc_commodity_table_get_table(book), "template", "template") ); xaccAccountSetType( sx->template_acct, ACCT_TYPE_BANK ); xaccAccountCommitEdit( sx->template_acct ); ra = gnc_book_get_template_root( book ); gnc_account_append_child( ra, sx->template_acct ); }
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); }
void gncOwnerAutoApplyPaymentsWithLots (const GncOwner *owner, GList *lots) { GList *left_iter; /* General note: in the code below the term "payment" can * both mean a true payment or a document of * the opposite sign (invoice vs credit note) relative to * the lot being processed. In general this function will * perform a balancing action on a set of lots, so you * will also find frequent references to balancing instead. */ /* Payments can only be applied when at least an owner * and a list of lots to use are given */ if (!owner) return; if (!lots) return; for (left_iter = lots; left_iter; left_iter = left_iter->next) { GNCLot *left_lot = left_iter->data; gnc_numeric left_lot_bal; gboolean left_lot_has_doc; gboolean left_modified = FALSE; Account *acct; GList *right_iter; /* Only attempt to apply payments to open lots. * Note that due to the iterative nature of this function lots * in the list may become empty/closed before they are evaluated as * base lot, so we should check this for each lot. */ if (!left_lot || qof_instance_get_destroying (left_lot)) continue; if (gnc_lot_count_splits (left_lot) == 0) { gnc_lot_destroy (left_lot); continue; } if (gnc_lot_is_closed (left_lot)) continue; acct = gnc_lot_get_account (left_lot); xaccAccountBeginEdit (acct); left_lot_bal = gnc_lot_get_balance (left_lot); left_lot_has_doc = (gncInvoiceGetInvoiceFromLot (left_lot) != NULL); /* Attempt to offset left_lot with any of the remaining lots. To do so * iterate over the remaining lots adding lot links or moving payments * around. */ for (right_iter = left_iter->next; right_iter; right_iter = right_iter->next) { GNCLot *right_lot = right_iter->data; gnc_numeric right_lot_bal; gboolean right_lot_has_doc; /* Only attempt to use open lots to balance the base lot. * Note that due to the iterative nature of this function lots * in the list may become empty/closed before they are evaluated as * base lot, so we should check this for each lot. */ if (!right_lot || qof_instance_get_destroying (right_lot)) continue; if (gnc_lot_count_splits (right_lot) == 0) { gnc_lot_destroy (right_lot); continue; } if (gnc_lot_is_closed (right_lot)) continue; /* Balancing transactions for invoice/payments can only happen * in the same account. */ if (acct != gnc_lot_get_account (right_lot)) continue; /* Only attempt to balance if the base lot and balancing lot are * of the opposite sign. (Otherwise we would increase the balance * of the lot - Duh */ right_lot_bal = gnc_lot_get_balance (right_lot); if (gnc_numeric_positive_p (left_lot_bal) == gnc_numeric_positive_p (right_lot_bal)) continue; /* Ok we found two lots than can (partly) offset each other. * Depending on the lot types, a different action is needed to accomplish this. * 1. Both lots are document lots (invoices/credit notes) * -> Create a lot linking transaction between the lots * 2. Both lots are payment lots (lots without a document attached) * -> Use part of the bigger lot to the close the smaller lot * 3. One document lot with one payment lot * -> Use (part of) the payment to offset (part of) the document lot, * Which one will be closed depends on which is the bigger one */ right_lot_has_doc = (gncInvoiceGetInvoiceFromLot (right_lot) != NULL); if (left_lot_has_doc && right_lot_has_doc) gncOwnerCreateLotLink (left_lot, right_lot, owner); else if (!left_lot_has_doc && !right_lot_has_doc) { gint cmp = gnc_numeric_compare (gnc_numeric_abs (left_lot_bal), gnc_numeric_abs (right_lot_bal)); if (cmp >= 0) gncOwnerOffsetLots (left_lot, right_lot, owner); else gncOwnerOffsetLots (right_lot, left_lot, owner); } else { GNCLot *doc_lot = left_lot_has_doc ? left_lot : right_lot; GNCLot *pay_lot = left_lot_has_doc ? right_lot : left_lot; // Ok, let's try to move a payment from pay_lot to doc_lot gncOwnerOffsetLots (pay_lot, doc_lot, owner); } /* If we get here, then right_lot was modified * If the lot has a document, send an event for send an event for it as well * so it gets potentially updated as paid */ { GncInvoice *this_invoice = gncInvoiceGetInvoiceFromLot(right_lot); if (this_invoice) qof_event_gen (QOF_INSTANCE(this_invoice), QOF_EVENT_MODIFY, NULL); } left_modified = TRUE; } /* If left_lot was modified and the lot has a document, * send an event for send an event for it as well * so it gets potentially updated as paid */ if (left_modified) { GncInvoice *this_invoice = gncInvoiceGetInvoiceFromLot(left_lot); if (this_invoice) qof_event_gen (QOF_INSTANCE(this_invoice), QOF_EVENT_MODIFY, NULL); } xaccAccountCommitEdit (acct); } }
/** * Executes a transaction query statement and loads the transactions and all * of the splits. * * @param be SQL backend * @param stmt SQL statement */ static void query_transactions( GncSqlBackend* be, GncSqlStatement* stmt ) { GncSqlResult* result; g_return_if_fail( be != NULL ); g_return_if_fail( stmt != NULL ); result = gnc_sql_execute_select_statement( be, stmt ); if ( result != NULL ) { GList* tx_list = NULL; GList* node; GncSqlRow* row; Transaction* tx; #if LOAD_TRANSACTIONS_AS_NEEDED GSList* bal_list = NULL; GSList* nextbal; Account* root = gnc_book_get_root_account( be->book ); qof_event_suspend(); xaccAccountBeginEdit( root ); // Save the start/ending balances (balance, cleared and reconciled) for // every account. gnc_account_foreach_descendant( gnc_book_get_root_account( be->primary_book ), save_account_balances, &bal_list ); #endif // Load the transactions row = gnc_sql_result_get_first_row( result ); while ( row != NULL ) { tx = load_single_tx( be, row ); if ( tx != NULL ) { tx_list = g_list_prepend( tx_list, tx ); } row = gnc_sql_result_get_next_row( result ); } gnc_sql_result_dispose( result ); // Load all splits and slots for the transactions if ( tx_list != NULL ) { gnc_sql_slots_load_for_list( be, tx_list ); load_splits_for_tx_list( be, tx_list ); } // Commit all of the transactions for ( node = tx_list; node != NULL; node = node->next ) { Transaction* pTx = GNC_TRANSACTION(node->data); xaccTransCommitEdit( pTx ); } g_list_free( tx_list ); #if LOAD_TRANSACTIONS_AS_NEEDED // Update the account balances based on the loaded splits. If the end // balance has changed, update the start balance so that the end // balance is the same as it was before the splits were loaded. // Repeat for cleared and reconciled balances. for ( nextbal = bal_list; nextbal != NULL; nextbal = nextbal->next ) { full_acct_balances_t* balns = (full_acct_balances_t*)nextbal->data; gnc_numeric* pnew_end_bal; gnc_numeric* pnew_end_c_bal; gnc_numeric* pnew_end_r_bal; gnc_numeric adj; g_object_get( balns->acc, "end-balance", &pnew_end_bal, "end-cleared-balance", &pnew_end_c_bal, "end-reconciled-balance", &pnew_end_r_bal, NULL ); if ( !gnc_numeric_eq( *pnew_end_bal, balns->end_bal ) ) { adj = gnc_numeric_sub( balns->end_bal, *pnew_end_bal, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD ); balns->start_bal = gnc_numeric_add( balns->start_bal, adj, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD ); g_object_set( balns->acc, "start-balance", &balns->start_bal, NULL ); } if ( !gnc_numeric_eq( *pnew_end_c_bal, balns->end_cleared_bal ) ) { adj = gnc_numeric_sub( balns->end_cleared_bal, *pnew_end_c_bal, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD ); balns->start_cleared_bal = gnc_numeric_add( balns->start_cleared_bal, adj, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD ); g_object_set( balns->acc, "start-cleared-balance", &balns->start_cleared_bal, NULL ); } if ( !gnc_numeric_eq( *pnew_end_r_bal, balns->end_reconciled_bal ) ) { adj = gnc_numeric_sub( balns->end_reconciled_bal, *pnew_end_r_bal, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD ); balns->start_reconciled_bal = gnc_numeric_add( balns->start_reconciled_bal, adj, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD ); g_object_set( balns->acc, "start-reconciled-balance", &balns->start_reconciled_bal, NULL ); } xaccAccountRecomputeBalance( balns->acc ); g_free( pnew_end_bal ); g_free( pnew_end_c_bal ); g_free( pnew_end_r_bal ); g_free( balns ); } if ( bal_list != NULL ) { g_slist_free( bal_list ); } xaccAccountCommitEdit( root ); qof_event_resume(); #endif } }
void gnc_stock_split_assistant_finish (GtkAssistant *assistant, gpointer user_data) { StockSplitInfo *info = user_data; GList *account_commits; GList *node; gnc_numeric amount; Transaction *trans; Account *account; Split *split; time64 date; account = info->acct; g_return_if_fail (account != NULL); amount = gnc_amount_edit_get_amount (GNC_AMOUNT_EDIT (info->distribution_edit)); g_return_if_fail (!gnc_numeric_zero_p (amount)); gnc_suspend_gui_refresh (); trans = xaccMallocTransaction (gnc_get_current_book ()); xaccTransBeginEdit (trans); xaccTransSetCurrency (trans, gnc_default_currency ()); date = gnc_date_edit_get_date (GNC_DATE_EDIT (info->date_edit)); xaccTransSetDatePostedSecsNormalized (trans, date); { const char *description; description = gtk_entry_get_text (GTK_ENTRY (info->description_entry)); xaccTransSetDescription (trans, description); } split = xaccMallocSplit (gnc_get_current_book ()); xaccAccountBeginEdit (account); account_commits = g_list_prepend (NULL, account); xaccTransAppendSplit (trans, split); xaccAccountInsertSplit (account, split); xaccSplitSetAmount (split, amount); xaccSplitMakeStockSplit (split); /* Set split-action with gnc_set_num_action which is the same as * xaccSplitSetAction with these arguments */ /* Translators: This string has a disambiguation prefix */ gnc_set_num_action (NULL, split, NULL, Q_("Action Column|Split")); amount = gnc_amount_edit_get_amount (GNC_AMOUNT_EDIT (info->price_edit)); if (gnc_numeric_positive_p (amount)) { QofBook *book; GNCPrice *price; GNCPriceDB *pdb; GNCCurrencyEdit *ce; Timespec ts; ce = GNC_CURRENCY_EDIT (info->price_currency_edit); ts.tv_sec = date; ts.tv_nsec = 0; price = gnc_price_create (gnc_get_current_book ()); gnc_price_begin_edit (price); gnc_price_set_commodity (price, xaccAccountGetCommodity (account)); gnc_price_set_currency (price, gnc_currency_edit_get_currency (ce)); gnc_price_set_time (price, ts); gnc_price_set_source (price, PRICE_SOURCE_STOCK_SPLIT); gnc_price_set_typestr (price, PRICE_TYPE_UNK); gnc_price_set_value (price, amount); gnc_price_commit_edit (price); book = gnc_get_current_book (); pdb = gnc_pricedb_get_db (book); if (!gnc_pricedb_add_price (pdb, price)) gnc_error_dialog (info->window, "%s", _("Error adding price.")); } amount = gnc_amount_edit_get_amount (GNC_AMOUNT_EDIT (info->cash_edit)); if (gnc_numeric_positive_p (amount)) { const char *memo; memo = gtk_entry_get_text (GTK_ENTRY (info->memo_entry)); /* asset split */ account = gnc_tree_view_account_get_selected_account (GNC_TREE_VIEW_ACCOUNT(info->asset_tree)); split = xaccMallocSplit (gnc_get_current_book ()); xaccAccountBeginEdit (account); account_commits = g_list_prepend (account_commits, account); xaccAccountInsertSplit (account, split); xaccTransAppendSplit (trans, split); xaccSplitSetAmount (split, amount); xaccSplitSetValue (split, amount); xaccSplitSetMemo (split, memo); /* income split */ account = gnc_tree_view_account_get_selected_account (GNC_TREE_VIEW_ACCOUNT(info->income_tree)); split = xaccMallocSplit (gnc_get_current_book ()); xaccAccountBeginEdit (account); account_commits = g_list_prepend (account_commits, account); xaccAccountInsertSplit (account, split); xaccTransAppendSplit (trans, split); xaccSplitSetAmount (split, gnc_numeric_neg (amount)); xaccSplitSetValue (split, gnc_numeric_neg (amount)); xaccSplitSetMemo (split, memo); } xaccTransCommitEdit (trans); for (node = account_commits; node; node = node->next) xaccAccountCommitEdit (node->data); g_list_free (account_commits); gnc_resume_gui_refresh (); gnc_close_gui_component_by_data (ASSISTANT_STOCK_SPLIT_CM_CLASS, info); }
static void delete_random_account (Account* act) { xaccAccountBeginEdit (act); xaccAccountDestroy (act); }
static void test_transaction (void) { int i; for (i = 0; i < 50; i++) { Transaction* ran_trn; xmlNodePtr test_node; gnc_commodity* com, *new_com; gchar* filename1; int fd; /* The next line exists for its side effect of creating the * account tree. */ get_random_account_tree (book); ran_trn = get_random_transaction (book); new_com = get_random_commodity (book); if (!ran_trn) { failure_args ("transaction_xml", __FILE__, __LINE__, "get_random_transaction returned NULL"); return; } { /* xaccAccountInsertSplit can reorder the splits. */ GList* list = g_list_copy (xaccTransGetSplitList (ran_trn)); GList* node = list; for (; node; node = node->next) { Split* s = static_cast<decltype (s)> (node->data); Account* a = xaccMallocAccount (book); xaccAccountBeginEdit (a); xaccAccountSetCommodity (a, new_com); xaccAccountSetCommoditySCU (a, xaccSplitGetAmount (s).denom); xaccAccountInsertSplit (a, s); xaccAccountCommitEdit (a); } g_list_free (list); } com = xaccTransGetCurrency (ran_trn); test_node = gnc_transaction_dom_tree_create (ran_trn); if (!test_node) { failure_args ("transaction_xml", __FILE__, __LINE__, "gnc_transaction_dom_tree_create returned NULL"); really_get_rid_of_transaction (ran_trn); continue; } auto compare_msg = node_and_transaction_equal (test_node, ran_trn); if (compare_msg != nullptr) { failure_args ("transaction_xml", __FILE__, __LINE__, "node and transaction were not equal: %s", compare_msg); xmlElemDump (stdout, NULL, test_node); printf ("\n"); fflush (stdout); xmlFreeNode (test_node); really_get_rid_of_transaction (ran_trn); continue; } else { success_args ("transaction_xml", __FILE__, __LINE__, "%d", i); } filename1 = g_strdup_printf ("test_file_XXXXXX"); fd = g_mkstemp (filename1); write_dom_node_to_file (test_node, fd); close (fd); { GList* node = xaccTransGetSplitList (ran_trn); for (; node; node = node->next) { Split* s = static_cast<decltype (s)> (node->data); Account* a1 = xaccSplitGetAccount (s); Account* a2 = xaccMallocAccount (book); xaccAccountBeginEdit (a2); xaccAccountSetCommoditySCU (a2, xaccAccountGetCommoditySCU (a1)); xaccAccountSetGUID (a2, xaccAccountGetGUID (a1)); xaccAccountCommitEdit (a2); } } { sixtp* parser; tran_data data; const char* msg = "[xaccAccountScrubCommodity()] Account \"\" does not have a commodity!"; const char* logdomain = "gnc.engine.scrub"; GLogLevelFlags loglevel = static_cast<decltype (loglevel)> (G_LOG_LEVEL_CRITICAL); TestErrorStruct check = { loglevel, const_cast<char*> (logdomain), const_cast<char*> (msg) }; g_log_set_handler (logdomain, loglevel, (GLogFunc)test_checked_handler, &check); data.trn = ran_trn; data.com = com; data.value = i; parser = gnc_transaction_sixtp_parser_create (); if (!gnc_xml_parse_file (parser, filename1, test_add_transaction, (gpointer)&data, book)) { failure_args ("gnc_xml_parse_file returned FALSE", __FILE__, __LINE__, "%d", i); } else really_get_rid_of_transaction (data.new_trn); } /* no handling of circular data structures. We'll do that later */ /* sixtp_destroy(parser); */ g_unlink (filename1); g_free (filename1); really_get_rid_of_transaction (ran_trn); xmlFreeNode (test_node); } }
static void test_transaction(void) { int i; for (i = 0; i < 50; i++) { Transaction *ran_trn; Account *root; xmlNodePtr test_node; gnc_commodity *com, *new_com; gchar *compare_msg; gchar *filename1; int fd; /* The next line exists for its side effect of creating the * account tree. */ root = get_random_account_tree(book); ran_trn = get_random_transaction(book); new_com = get_random_commodity( book ); if (!ran_trn) { failure_args("transaction_xml", __FILE__, __LINE__, "get_random_transaction returned NULL"); return; } { /* xaccAccountInsertSplit can reorder the splits. */ GList * list = g_list_copy(xaccTransGetSplitList (ran_trn)); GList * node = list; for ( ; node; node = node->next) { Split * s = node->data; Account * a = xaccMallocAccount(book); xaccAccountBeginEdit (a); xaccAccountSetCommodity( a, new_com ); xaccAccountSetCommoditySCU (a, xaccSplitGetAmount (s).denom); xaccAccountInsertSplit (a, s); xaccAccountCommitEdit (a); } g_list_free(list); } com = xaccTransGetCurrency (ran_trn); test_node = gnc_transaction_dom_tree_create(ran_trn); if (!test_node) { failure_args("transaction_xml", __FILE__, __LINE__, "gnc_transaction_dom_tree_create returned NULL"); really_get_rid_of_transaction(ran_trn); continue; } if ((compare_msg = node_and_transaction_equal(test_node, ran_trn)) != NULL) { failure_args("transaction_xml", __FILE__, __LINE__, "node and transaction were not equal: %s", compare_msg); xmlElemDump(stdout, NULL, test_node); printf("\n"); fflush(stdout); xmlFreeNode(test_node); really_get_rid_of_transaction(ran_trn); continue; } else { success_args("transaction_xml", __FILE__, __LINE__, "%d", i ); } filename1 = g_strdup_printf("test_file_XXXXXX"); fd = g_mkstemp(filename1); write_dom_node_to_file(test_node, fd); close(fd); { GList * node = xaccTransGetSplitList (ran_trn); for ( ; node; node = node->next) { Split * s = node->data; Account * a1 = xaccSplitGetAccount(s); Account * a2 = xaccMallocAccount(book); xaccAccountBeginEdit (a2); xaccAccountSetCommoditySCU (a2, xaccAccountGetCommoditySCU (a1)); xaccAccountSetGUID (a2, xaccAccountGetGUID (a1)); xaccAccountCommitEdit (a2); } } { sixtp *parser; tran_data data; data.trn = ran_trn; data.com = com; data.value = i; g_print(" There will follow a bunch of CRIT scrub errors about the account not having a commodity. There isn't an account in the XML, so of course not. Ignore the errors\n"); parser = gnc_transaction_sixtp_parser_create(); if (!gnc_xml_parse_file(parser, filename1, test_add_transaction, (gpointer)&data, book)) { failure_args("gnc_xml_parse_file returned FALSE", __FILE__, __LINE__, "%d", i); } else really_get_rid_of_transaction (data.new_trn); /* no handling of circular data structures. We'll do that later */ /* sixtp_destroy(parser); */ } g_unlink(filename1); g_free(filename1); really_get_rid_of_transaction(ran_trn); xmlFreeNode(test_node); } }
GNCLot * gncOwnerCreatePaymentLot (const GncOwner *owner, Transaction *txn, Account *posted_acc, Account *xfer_acc, gnc_numeric amount, gnc_numeric exch, Timespec date, const char *memo, const char *num) { QofBook *book; Split *split; const char *name; gnc_commodity *commodity; Split *xfer_split = NULL; GNCLot *payment_lot; /* Verify our arguments */ if (!owner || !posted_acc || !xfer_acc) return NULL; g_return_val_if_fail (owner->owner.undefined != NULL, NULL); /* Compute the ancillary data */ book = gnc_account_get_book (posted_acc); name = gncOwnerGetName (gncOwnerGetEndOwner ((GncOwner*)owner)); commodity = gncOwnerGetCurrency (owner); // reverse = use_reversed_payment_amounts(owner); if (txn) { /* Pre-existing transaction was specified. We completely clear it, * except for the split in the transfer account, unless the * transaction can't be reused (wrong currency, wrong transfer account). * In that case, the transaction is simply removed and an new * one created. */ xfer_split = xaccTransFindSplitByAccount(txn, xfer_acc); if (xaccTransGetCurrency(txn) != gncOwnerGetCurrency (owner)) { g_message("Uh oh, mismatching currency/commodity between selected transaction and owner. We fall back to manual creation of a new transaction."); xfer_split = NULL; } if (!xfer_split) { g_message("Huh? Asset account not found anymore. Fully deleting old txn and now creating a new one."); xaccTransBeginEdit (txn); xaccTransDestroy (txn); xaccTransCommitEdit (txn); txn = NULL; } else { int i = 0; xaccTransBeginEdit (txn); while (i < xaccTransCountSplits(txn)) { Split *split = xaccTransGetSplit (txn, i); if (split == xfer_split) { gnc_set_num_action (NULL, split, num, _("Payment")); ++i; } else { xaccSplitDestroy(split); } } /* Note: don't commit transaction now - that would insert an imbalance split.*/ } } /* Create the transaction if we don't have one yet */ if (!txn) { txn = xaccMallocTransaction (book); xaccTransBeginEdit (txn); } /* Insert a split for the transfer account if we don't have one yet */ if (!xfer_split) { /* Set up the transaction */ xaccTransSetDescription (txn, name ? name : ""); /* set per book option */ xaccTransSetCurrency (txn, commodity); xaccTransSetDateEnteredSecs (txn, gnc_time (NULL)); xaccTransSetDatePostedTS (txn, &date); /* The split for the transfer account */ split = xaccMallocSplit (book); xaccSplitSetMemo (split, memo); /* set per book option */ gnc_set_num_action (NULL, split, num, _("Payment")); xaccAccountBeginEdit (xfer_acc); xaccAccountInsertSplit (xfer_acc, split); xaccAccountCommitEdit (xfer_acc); xaccTransAppendSplit (txn, split); if (gnc_commodity_equal(xaccAccountGetCommodity(xfer_acc), commodity)) { xaccSplitSetBaseValue (split, amount, commodity); } else { /* Need to value the payment in terms of the owner commodity */ gnc_numeric payment_value = gnc_numeric_mul(amount, exch, GNC_DENOM_AUTO, GNC_HOW_RND_ROUND_HALF_UP); xaccSplitSetAmount(split, amount); xaccSplitSetValue(split, payment_value); } } /* Add a split in the post account */ split = xaccMallocSplit (book); xaccSplitSetMemo (split, memo); /* set per book option */ gnc_set_num_action (NULL, split, num, _("Payment")); xaccAccountBeginEdit (posted_acc); xaccAccountInsertSplit (posted_acc, split); xaccAccountCommitEdit (posted_acc); xaccTransAppendSplit (txn, split); xaccSplitSetBaseValue (split, gnc_numeric_neg (amount), commodity); /* Create a new lot for the payment */ payment_lot = gnc_lot_new (book); gncOwnerAttachToLot (owner, payment_lot); gnc_lot_add_split (payment_lot, split); /* Mark the transaction as a payment */ gnc_set_num_action (txn, NULL, num, _("Payment")); xaccTransSetTxnType (txn, TXN_TYPE_PAYMENT); /* Commit this new transaction */ xaccTransCommitEdit (txn); return payment_lot; }
void gncOwnerAutoApplyPaymentsWithLots (const GncOwner *owner, GList *lots) { GList *base_iter; /* General note: in the code below the term "payment" can * both mean a true payment or a document of * the opposite sign (invoice vs credit note) relative to * the lot being processed. In general this function will * perform a balancing action on a set of lots, so you * will also find frequent references to balancing instead. */ /* Payments can only be applied when at least an owner * and a list of lots to use are given */ if (!owner) return; if (!lots) return; for (base_iter = lots; base_iter; base_iter = base_iter->next) { GNCLot *base_lot = base_iter->data; QofBook *book; Account *acct; const gchar *name; GList *lot_list, *lot_iter; Transaction *txn = NULL; gnc_numeric base_lot_bal, val_to_pay, val_paid = { 0, 1 }; gboolean base_bal_is_pos; const gchar *action, *memo; /* Only attempt to apply payments to open lots. * Note that due to the iterative nature of this function lots * in the list may become closed before they are evaluated as * base lot, so we should check this for each lot. */ base_lot_bal = gnc_lot_get_balance (base_lot); if (gnc_numeric_zero_p (base_lot_bal)) continue; book = gnc_lot_get_book (base_lot); acct = gnc_lot_get_account (base_lot); name = gncOwnerGetName (gncOwnerGetEndOwner (owner)); lot_list = base_iter->next; /* Strings used when creating splits later on. */ action = _("Lot Link"); memo = _("Internal link between invoice and payment lots"); /* Note: to balance the lot the payment to assign * must have the opposite sign of the existing lot balance */ val_to_pay = gnc_numeric_neg (base_lot_bal); base_bal_is_pos = gnc_numeric_positive_p (base_lot_bal); /* Create splits in a linking transaction between lots until * - either the invoice lot is balanced * - or there are no more balancing lots. */ for (lot_iter = lot_list; lot_iter; lot_iter = lot_iter->next) { gnc_numeric payment_lot_balance; Split *split; Account *bal_acct; gnc_numeric split_amt; GNCLot *balancing_lot = lot_iter->data; /* Only attempt to use open lots to balance the base lot. * Note that due to the iterative nature of this function lots * in the list may become closed before they are evaluated as * base lot, so we should check this for each lot. */ if (gnc_lot_is_closed (balancing_lot)) continue; /* Balancing transactions for invoice/payments can only happen * in the same account. */ bal_acct = gnc_lot_get_account (balancing_lot); if (acct != bal_acct) continue; payment_lot_balance = gnc_lot_get_balance (balancing_lot); /* Only attempt to balance if the base lot and balancing lot are * of the opposite sign. (Otherwise we would increase the balance * of the lot - Duh */ if (base_bal_is_pos == gnc_numeric_positive_p (payment_lot_balance)) continue; /* * If there is less to pay than there's open in the lot; we're done -- apply the base_lot_vale. * Note that payment_value and balance are opposite in sign, so we have to compare absolute values here * * Otherwise, apply the balance, subtract that from the payment_value, * and move on to the next one. */ if (gnc_numeric_compare (gnc_numeric_abs (val_to_pay), gnc_numeric_abs (payment_lot_balance)) <= 0) { /* abs(val_to_pay) <= abs(balance) */ split_amt = val_to_pay; } else { /* abs(val_to_pay) > abs(balance) * Remember payment_value and balance are opposite in sign, * and we want a payment to neutralize the current balance * so we need to negate here */ split_amt = payment_lot_balance; } /* If not created yet, create a new transaction linking * the base lot and the balancing lot(s) */ if (!txn) { Timespec ts = xaccTransRetDatePostedTS (xaccSplitGetParent (gnc_lot_get_latest_split (base_lot))); xaccAccountBeginEdit (acct); txn = xaccMallocTransaction (book); xaccTransBeginEdit (txn); xaccTransSetDescription (txn, name ? name : ""); xaccTransSetCurrency (txn, xaccAccountGetCommodity(acct)); xaccTransSetDateEnteredSecs (txn, gnc_time (NULL)); xaccTransSetDatePostedTS (txn, &ts); xaccTransSetTxnType (txn, TXN_TYPE_LINK); } /* Create the split for this link in current balancing lot */ split = xaccMallocSplit (book); xaccSplitSetMemo (split, memo); /* set Action using utility function */ gnc_set_num_action (NULL, split, NULL, action); xaccAccountInsertSplit (acct, split); xaccTransAppendSplit (txn, split); xaccSplitSetBaseValue (split, gnc_numeric_neg (split_amt), xaccAccountGetCommodity(acct)); gnc_lot_add_split (balancing_lot, split); /* If the balancing lot was linked to a document (invoice/credit note), * send an event for it as well so it gets potentially updated as paid */ { GncInvoice *this_invoice = gncInvoiceGetInvoiceFromLot(balancing_lot); if (this_invoice) qof_event_gen (QOF_INSTANCE(this_invoice), QOF_EVENT_MODIFY, NULL); } val_paid = gnc_numeric_add (val_paid, split_amt, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); val_to_pay = gnc_numeric_sub (val_to_pay, split_amt, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); if (gnc_numeric_zero_p (val_to_pay)) break; } /* If the above loop managed to create a transaction and some balancing splits, * create the final split for the link transaction in the base lot */ if (txn) { GncInvoice *this_invoice; Split *split = xaccMallocSplit (book); xaccSplitSetMemo (split, memo); /* set Action with utiltity function */ gnc_set_num_action (NULL, split, NULL, action); xaccAccountInsertSplit (acct, split); xaccTransAppendSplit (txn, split); xaccSplitSetBaseValue (split, val_paid, xaccAccountGetCommodity(acct)); gnc_lot_add_split (base_lot, split); xaccTransCommitEdit (txn); xaccAccountCommitEdit (acct); /* If the base lot was linked to a document (invoice/credit note), * send an event for it as well so it gets potentially updated as paid */ this_invoice = gncInvoiceGetInvoiceFromLot(base_lot); if (this_invoice) qof_event_gen (QOF_INSTANCE(this_invoice), QOF_EVENT_MODIFY, NULL); } } }