33 #include <glib/gi18n.h> 40 #include "gncInvoiceP.h" 47 #define G_LOG_DOMAIN "gnc.engine.scrub" 52 gncScrubInvoiceState (GNCLot *lot)
55 GncInvoice *invoice = NULL;
60 Split *split = ls_iter->data;
61 Transaction *txn = NULL;
73 if (invoice != lot_invoice)
75 PINFO(
"Correcting lot invoice associaton. Old invoice: %p, new invoice %p", lot_invoice, invoice);
76 gncInvoiceDetachFromLot(lot);
79 gncInvoiceAttachToLot (invoice, lot);
94 static gboolean reduce_biggest_split (Split *splitA, Split *splitB)
120 scrub_other_link (GNCLot *from_lot, Split *ll_from_split,
121 GNCLot *to_lot, Split *ll_to_split)
123 Split *real_from_split;
124 gboolean modified = FALSE;
125 gnc_numeric real_from_val;
138 if (!real_from_split)
148 modified = reduce_biggest_split (ll_from_split, ll_to_split);
149 modified |= reduce_biggest_split (real_from_split, ll_from_split);
150 modified |= reduce_biggest_split (ll_from_split, ll_to_split);
159 PWARN(
"real_from_val (%s) and to_val (%s) differ. " 160 "This is unexpected! Skip scrubbing of real_from_split %p against ll_to_split %p.",
163 real_from_split, ll_to_split);
184 gncScrubLotLinks (GNCLot *scrub_lot)
186 gboolean modified = FALSE, restart_needed = FALSE;
190 restart_needed = FALSE;
195 Split *sl_split = sls_iter->data;
196 Transaction *ll_txn = NULL;
208 PWARN(
"Encountered a split in a business lot that's not part of any transaction. " 209 "This is unexpected! Skipping split %p.", sl_split);
229 Split *ll_txn_split = lts_iter->data;
230 GNCLot *remote_lot = NULL;
231 gboolean sl_is_doc_lot, rl_is_doc_lot;
237 if (sl_split == ll_txn_split)
272 if (sl_is_doc_lot && rl_is_doc_lot)
274 else if (!sl_is_doc_lot && !rl_is_doc_lot)
279 restart_needed = scrub_other_link (scrub_lot, sl_split, remote_lot, ll_txn_split);
281 restart_needed = scrub_other_link (remote_lot, ll_txn_split, scrub_lot, sl_split);
285 GNCLot *doc_lot = sl_is_doc_lot ? scrub_lot : remote_lot;
286 GNCLot *pay_lot = sl_is_doc_lot ? remote_lot : scrub_lot;
287 Split *ll_doc_split = sl_is_doc_lot ? sl_split : ll_txn_split;
288 Split *ll_pay_split = sl_is_doc_lot ? ll_txn_split : sl_split;
290 restart_needed = scrub_other_link (pay_lot, ll_pay_split, doc_lot, ll_doc_split);
315 gncSLFindOffsSplits (
SplitList *avail_splits, gnc_numeric target_value)
317 gint curr_recurse_level = 0;
318 gint max_recurse_level = g_list_length (avail_splits) - 1;
323 for (curr_recurse_level = 0;
324 curr_recurse_level <= max_recurse_level;
325 curr_recurse_level++)
328 for (split_iter = avail_splits; split_iter; split_iter = split_iter->next)
330 Split *split = split_iter->data;
332 gnc_numeric split_value, remaining_value;
340 if (curr_recurse_level == 0)
343 match_splits = g_list_prepend (NULL, split);
349 match_splits = gncSLFindOffsSplits (split_iter->next,
354 return g_list_prepend (match_splits, split);
363 gncScrubLotDanglingPayments (GNCLot *lot)
365 SplitList * split_list, *filtered_list = NULL, *match_list = NULL, *node;
379 for (node = split_list; node; node = node->next)
381 Split *free_split = node->data;
382 Transaction *free_trans;
383 gnc_numeric free_val;
403 filtered_list = g_list_prepend (filtered_list, free_split);
406 filtered_list = g_list_reverse (filtered_list);
407 match_list = gncSLFindOffsSplits (filtered_list, ll_val);
408 g_list_free (filtered_list);
410 for (node = match_list; node; node = node->next)
412 Split *match_split = node->data;
418 g_list_free (match_list);
426 gncScrubLotIsSingleLotLinkSplit (GNCLot *lot)
429 Transaction *trans = NULL;
435 if (1 != gnc_lot_count_splits (lot))
445 PWARN(
"Encountered a split in a business lot that's not part of any transaction. " 446 "This is unexpected! Skipping split %p.", split);
460 gboolean splits_deleted = FALSE;
461 gboolean dangling_payments = FALSE;
462 gboolean dangling_lot_link = FALSE;
466 if (!lot)
return FALSE;
468 ENTER (
"(lot=%p) %s", lot, lotname ? lotname :
"(no lotname)");
479 gncScrubInvoiceState (lot);
484 splits_deleted = gncScrubLotLinks (lot);
487 dangling_lot_link = gncScrubLotIsSingleLotLinkSplit (lot);
488 if (dangling_lot_link)
490 dangling_payments = gncScrubLotDanglingPayments (lot);
491 if (dangling_payments)
492 splits_deleted |= gncScrubLotLinks (lot);
502 if (0 == gnc_lot_count_splits (lot))
504 PINFO(
"All splits were removed from lot, deleting");
505 gnc_lot_destroy (lot);
511 LEAVE (
"(lot=%s, deleted=%d, dangling lot link=%d, dangling_payments=%d)",
512 lotname ? lotname :
"(no lotname)", splits_deleted, dangling_lot_link,
516 return splits_deleted;
523 gboolean deleted_split = FALSE;
525 if (!split)
return FALSE;
526 ENTER (
"(split=%p)", split);
536 Transaction *posted_txn = gncInvoiceGetPostedTxn (invoice);
547 if ((txntype ==
TXN_TYPE_NONE) && read_only && !is_void && lot)
549 const gchar *memo = _(
"Please delete this transaction. Explanation at https://wiki.gnucash.org/wiki/Business_Features_Issues#Double_posting");
551 xaccTransClearReadOnly (txn);
553 gnc_lot_remove_split (lot, split);
554 PWARN(
"Cleared double post status of transaction \"%s\", dated %s. " 555 "Please delete transaction and verify balance.",
563 else if (invoice && (txn != posted_txn))
565 const gchar *memo = _(
"Please delete this transaction. Explanation at https://wiki.gnucash.org/wiki/Business_Features_Issues#I_can.27t_delete_a_transaction_of_type_.22I.22_from_the_AR.2FAP_account");
567 xaccTransClearReadOnly (txn);
572 gnc_lot_remove_split (lot, split);
573 gncInvoiceDetachFromLot (lot);
576 PWARN(
"Cleared double post status of transaction \"%s\", dated %s. " 577 "Please delete transaction and verify balance.",
596 if (lot && (gnc_lot_count_splits (lot) == 0))
597 gnc_lot_destroy (lot);
599 deleted_split = TRUE;
604 LEAVE (
"(split=%p)", split);
605 return deleted_split;
615 gint curr_lot_no = 0;
617 const char *message = _(
"Checking business lots in account %s: %u of %u");
621 if (gnc_get_abort_scrub())
622 (percentagefunc)(NULL, -1.0);
627 str = str ? str :
"(null)";
629 ENTER (
"(acc=%s)", str);
630 PINFO (
"Cleaning up superfluous lot links in account %s\n", str);
634 lot_count = g_list_length (lots);
635 for (node = lots; node; node = node->next)
637 GNCLot *lot = node->data;
639 PINFO(
"Start processing lot %d of %d",
640 curr_lot_no + 1, lot_count);
642 if (curr_lot_no % 100 == 0)
644 char *progress_msg = g_strdup_printf (message, str, curr_lot_no, lot_count);
645 (percentagefunc)(progress_msg, (100 * curr_lot_no) / lot_count);
646 g_free (progress_msg);
652 PINFO(
"Finished processing lot %d of %d",
653 curr_lot_no + 1, lot_count);
658 (percentagefunc)(NULL, -1.0);
659 LEAVE (
"(acc=%s)", str);
668 gint split_count = 0;
671 const char *message = _(
"Checking business splits in account %s: %u of %u");
675 if (gnc_get_abort_scrub())
676 (percentagefunc)(NULL, -1.0);
681 str = str ? str :
"(null)";
683 ENTER (
"(acc=%s)", str);
684 PINFO (
"Cleaning up superfluous lot links in account %s\n", str);
690 split_count = g_list_length (splits);
691 for (node = splits; node; node = node->next)
693 Split *split = node->data;
695 PINFO(
"Start processing split %d of %d",
696 curr_split_no + 1, split_count);
698 if (gnc_get_abort_scrub ())
701 if (curr_split_no % 100 == 0)
703 char *progress_msg = g_strdup_printf (message, str, curr_split_no, split_count);
704 (percentagefunc)(progress_msg, (100 * curr_split_no) / split_count);
705 g_free (progress_msg);
714 PINFO(
"Finished processing split %d of %d",
715 curr_split_no + 1, split_count);
719 (percentagefunc)(NULL, -1.0);
720 LEAVE (
"(acc=%s)", str);
738 lot_scrub_cb (
Account *acc, gpointer data)
gboolean gnc_numeric_equal(gnc_numeric a, gnc_numeric b)
Equivalence predicate: Returns TRUE (1) if a and b represent the same number.
gboolean xaccScrubMergeLotSubSplits(GNCLot *lot, gboolean strict)
The xaccScrubMergeLotSubSplits() routine does the same as the xaccScrubMergSubSplits, except that it does it for all of the splits in the lot.
GList LotList
GList of GNCLots.
void(* QofPercentageFunc)(const char *message, double percent)
The qof_session_load() method causes the QofBook to be made ready to to use with this URL/datastore...
time64 xaccTransGetDate(const Transaction *trans)
Retrieve the posted date of the transaction.
SplitList * xaccAccountGetSplitList(const Account *acc)
The xaccAccountGetSplitList() routine returns a pointer to a GList of the splits in the account...
void gnc_account_foreach_descendant(const Account *acc, AccountCb thunk, gpointer user_data)
This method will traverse all children of this accounts and their descendants, calling 'func' on each...
char xaccTransGetTxnType(Transaction *trans)
Returns the Transaction Type: note this type will be derived from the transaction splits...
#define G_LOG_DOMAIN
Functions providing the SX List as a plugin page.
#define TXN_TYPE_INVOICE
Transaction is an invoice.
#define PINFO(format, args...)
Print an informational note.
GNCAccountType xaccAccountGetType(const Account *acc)
Returns the account's account type.
gboolean xaccSplitDestroy(Split *split)
Destructor.
Utilities to Convert Stock Accounts to use Lots.
gnc_numeric gnc_numeric_neg(gnc_numeric a)
Returns a newly created gnc_numeric that is the negative of the given gnc_numeric value...
void gncScrubBusinessAccountSplits(Account *acc, QofPercentageFunc percentagefunc)
The gncScrubBusinessAccountSplits() function will call gncScrubBusinessSplit() on each split in the g...
const char * xaccTransGetReadOnly(Transaction *trans)
Returns a non-NULL value if this Transaction was marked as read-only with some specific "reason" text...
void gnc_lot_add_split(GNCLot *lot, Split *split)
The gnc_lot_add_split() routine adds a split to this lot.
gnc_numeric gnc_numeric_add(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Return a+b.
gboolean gnc_numeric_zero_p(gnc_numeric a)
Returns 1 if the given gnc_numeric is 0 (zero), else returns 0.
Transaction * xaccSplitGetParent(const Split *split)
Returns the parent transaction of the split.
int gnc_numeric_compare(gnc_numeric a, gnc_numeric b)
Returns 1 if a>b, -1 if b>a, 0 if a == b.
gchar * gnc_numeric_to_string(gnc_numeric n)
Convert to string.
#define ENTER(format, args...)
Print a function entry debugging message.
Cleanup functions for business objects.
Split * gnc_lot_get_earliest_split(GNCLot *lot)
The gnc_lot_get_earliest_split() routine is a convenience routine that helps identify the earliest da...
void gncScrubBusinessAccountLots(Account *acc, QofPercentageFunc percentagefunc)
The gncScrubBusinessAccountLots() function will call gncScrubBusinessLot() on each lot in the given a...
GncInvoice * gncInvoiceGetInvoiceFromTxn(const Transaction *txn)
Given a transaction, find and return the Invoice.
const char * gnc_lot_get_title(const GNCLot *lot)
Get and set the account title, or the account notes, or the marker.
void xaccTransDestroy(Transaction *trans)
Destroys a transaction.
#define PWARN(format, args...)
Log a warning.
void xaccTransSetTxnType(Transaction *trans, char type)
Set the Transaction Type: note the type will be saved into the Transaction kvp property as a backward...
#define TXN_TYPE_NONE
No transaction type.
convert single-entry accounts to clean double-entry
char * qof_print_date(time64 secs)
Convenience; calls through to qof_print_date_dmy_buff().
GList SplitList
GList of Split.
Account handling public routines.
void xaccSplitSetMemo(Split *split, const char *memo)
The memo is an arbitrary string associated with a split.
gboolean gncOwnerReduceSplitTo(Split *split, gnc_numeric target_value)
Helper function to reduce the value of a split to target_value.
Implement Accounting Policy Private header File.
gboolean gncScrubBusinessSplit(Split *split)
The gncScrubBusinessSplit() function will fix all issues found with the given split.
SplitList * gnc_lot_get_split_list(const GNCLot *lot)
The gnc_lot_get_split_list() routine returns a GList of all the splits in this lot.
LotList * xaccAccountGetLotList(const Account *acc)
The xaccAccountGetLotList() routine returns a list of all lots in this account.
void gncScrubBusinessAccount(Account *acc, QofPercentageFunc percentagefunc)
The gncScrubBusinessAccount() function will call all scrub functions relevant for a given account on ...
const char * xaccTransGetDescription(const Transaction *trans)
Gets the transaction Description.
void gncOwnerAttachToLot(const GncOwner *owner, GNCLot *lot)
Attach an owner to a lot.
gboolean xaccAccountIsAPARType(GNCAccountType t)
Convenience function to check if the account is a valid business account type (meaning an Accounts Pa...
gnc_numeric gnc_numeric_abs(gnc_numeric a)
Returns a newly created gnc_numeric that is the absolute value of the given gnc_numeric value...
Find the least common multiple of the arguments' denominators and use that as the denominator of the ...
void xaccTransCommitEdit(Transaction *trans)
The xaccTransCommitEdit() method indicates that the changes to the transaction and its splits are com...
#define TXN_TYPE_LINK
Transaction is a link between (invoice and payment) lots.
void xaccTransBeginEdit(Transaction *trans)
The xaccTransBeginEdit() method must be called before any changes are made to a transaction or any of...
All type declarations for the whole Gnucash engine.
gboolean gnc_numeric_positive_p(gnc_numeric a)
Returns 1 if a > 0, otherwise returns 0.
time64 xaccTransGetDateEntered(const Transaction *trans)
Retrieve the date of when the transaction was entered.
GncInvoice * gncInvoiceGetInvoiceFromLot(GNCLot *lot)
Given a LOT, find and return the Invoice attached to the lot.
Business Invoice Interface.
gboolean xaccTransGetVoidStatus(const Transaction *trans)
Retrieve information on whether or not a transaction has been voided.
gnc_numeric xaccSplitGetValue(const Split *split)
Returns the value of this split in the transaction's commodity.
gboolean gncScrubBusinessLot(GNCLot *lot)
The gncScrubBusinessLot() function makes sure that the indicated lot has all the correct properties r...
void xaccAccountBeginEdit(Account *acc)
The xaccAccountBeginEdit() subroutine is the first phase of a two-phase-commit wrapper for account up...
#define LEAVE(format, args...)
Print a function exit debugging message.
Split * gncOwnerFindOffsettingSplit(GNCLot *lot, gnc_numeric target_value)
Helper function to find a split in lot that best offsets target_value Obviously it should be of oppos...
void gncOwnerSetLotLinkMemo(Transaction *ll_txn)
To help a user understand what a lot link transaction does, we set the memo to name all documents inv...
gint64 time64
Many systems, including Microsoft Windows and BSD-derived Unixes like Darwin, are retaining the int-3...
Account * gnc_lot_get_account(const GNCLot *lot)
The gnc_lot_get_account() routine returns the account with which this lot is associated.
void gncScrubBusinessAccountTree(Account *acc, QofPercentageFunc percentagefunc)
The gncScrubBusinessAccountTreeLots() function will call gncScrubBusinessAccount() on the given accou...
char * gnc_ctime(const time64 *secs)
Return a string representation of a date from a 64-bit time value.
const char * xaccAccountGetName(const Account *acc)
Get the account's name.
#define GNC_DENOM_AUTO
Values that can be passed as the 'denom' argument.
API for Transactions and Splits (journal entries)
void xaccAccountCommitEdit(Account *acc)
ThexaccAccountCommitEdit() subroutine is the second phase of a two-phase-commit wrapper for account u...
SplitList * xaccTransGetSplitList(const Transaction *trans)
The xaccTransGetSplitList() method returns a GList of the splits in a transaction.
GNCLot * xaccSplitGetLot(const Split *split)
Returns the pointer to the debited/credited Lot where this split belongs to, or NULL if it doesn't be...
gnc_numeric xaccSplitGetAmount(const Split *split)
Returns the amount of the split in the account's commodity.