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);
220 GList *tmp_iter = sls_iter->prev;
221 PINFO(
"Removing 0-value split from the lot.");
237 Split *ll_txn_split = lts_iter->data;
238 GNCLot *remote_lot = NULL;
239 gboolean sl_is_doc_lot, rl_is_doc_lot;
245 if (sl_split == ll_txn_split)
280 if (sl_is_doc_lot && rl_is_doc_lot)
282 else if (!sl_is_doc_lot && !rl_is_doc_lot)
287 restart_needed = scrub_other_link (scrub_lot, sl_split, remote_lot, ll_txn_split);
289 restart_needed = scrub_other_link (remote_lot, ll_txn_split, scrub_lot, sl_split);
293 GNCLot *doc_lot = sl_is_doc_lot ? scrub_lot : remote_lot;
294 GNCLot *pay_lot = sl_is_doc_lot ? remote_lot : scrub_lot;
295 Split *ll_doc_split = sl_is_doc_lot ? sl_split : ll_txn_split;
296 Split *ll_pay_split = sl_is_doc_lot ? ll_txn_split : sl_split;
298 restart_needed = scrub_other_link (pay_lot, ll_pay_split, doc_lot, ll_doc_split);
323 gncSLFindOffsSplits (
SplitList *avail_splits, gnc_numeric target_value)
325 gint curr_recurse_level = 0;
326 gint max_recurse_level = g_list_length (avail_splits) - 1;
331 for (curr_recurse_level = 0;
332 curr_recurse_level <= max_recurse_level;
333 curr_recurse_level++)
336 for (split_iter = avail_splits; split_iter; split_iter = split_iter->next)
338 Split *split = split_iter->data;
340 gnc_numeric split_value, remaining_value;
348 if (curr_recurse_level == 0)
351 match_splits = g_list_prepend (NULL, split);
357 match_splits = gncSLFindOffsSplits (split_iter->next,
362 return g_list_prepend (match_splits, split);
371 gncScrubLotDanglingPayments (GNCLot *lot)
373 SplitList * split_list, *filtered_list = NULL, *match_list = NULL, *node;
387 for (node = split_list; node; node = node->next)
389 Split *free_split = node->data;
390 Transaction *free_trans;
391 gnc_numeric free_val;
411 filtered_list = g_list_prepend (filtered_list, free_split);
413 g_list_free (split_list);
415 filtered_list = g_list_reverse (filtered_list);
416 match_list = gncSLFindOffsSplits (filtered_list, ll_val);
417 g_list_free (filtered_list);
419 for (node = match_list; node; node = node->next)
421 Split *match_split = node->data;
427 g_list_free (match_list);
435 gncScrubLotIsSingleLotLinkSplit (GNCLot *lot)
438 Transaction *trans = NULL;
444 if (1 != gnc_lot_count_splits (lot))
454 PWARN(
"Encountered a split in a business lot that's not part of any transaction. " 455 "This is unexpected! Skipping split %p.", split);
469 gboolean splits_deleted = FALSE;
470 gboolean dangling_payments = FALSE;
471 gboolean dangling_lot_link = FALSE;
475 if (!lot)
return FALSE;
476 lotname = g_strdup (gnc_lot_get_title (lot));
477 ENTER (
"(lot=%p) %s", lot, lotname ? lotname :
"(no lotname)");
488 gncScrubInvoiceState (lot);
493 splits_deleted = gncScrubLotLinks (lot);
496 dangling_lot_link = gncScrubLotIsSingleLotLinkSplit (lot);
497 if (dangling_lot_link)
499 dangling_payments = gncScrubLotDanglingPayments (lot);
500 if (dangling_payments)
501 splits_deleted |= gncScrubLotLinks (lot);
511 if (0 == gnc_lot_count_splits (lot))
513 PINFO(
"All splits were removed from lot, deleting");
514 gnc_lot_destroy (lot);
520 LEAVE (
"(lot=%s, deleted=%d, dangling lot link=%d, dangling_payments=%d)",
521 lotname ? lotname :
"(no lotname)", splits_deleted, dangling_lot_link,
525 return splits_deleted;
532 gboolean deleted_split = FALSE;
534 if (!split)
return FALSE;
535 ENTER (
"(split=%p)", split);
545 Transaction *posted_txn = gncInvoiceGetPostedTxn (invoice);
556 if ((txntype ==
TXN_TYPE_NONE) && read_only && !is_void && lot)
558 const gchar *memo = _(
"Please delete this transaction. Explanation at https://wiki.gnucash.org/wiki/Business_Features_Issues#Double_posting");
560 xaccTransClearReadOnly (txn);
563 PWARN(
"Cleared double post status of transaction \"%s\", dated %s. " 564 "Please delete transaction and verify balance.",
572 else if (invoice && (txn != posted_txn))
574 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");
576 xaccTransClearReadOnly (txn);
582 gncInvoiceDetachFromLot (lot);
585 PWARN(
"Cleared double post status of transaction \"%s\", dated %s. " 586 "Please delete transaction and verify balance.",
605 if (lot && (gnc_lot_count_splits (lot) == 0))
606 gnc_lot_destroy (lot);
608 deleted_split = TRUE;
613 LEAVE (
"(split=%p)", split);
614 return deleted_split;
624 gint curr_lot_no = 0;
626 const char *message = _(
"Checking business lots in account %s: %u of %u");
630 if (gnc_get_abort_scrub())
631 (percentagefunc)(NULL, -1.0);
636 str = str ? str :
"(null)";
638 ENTER (
"(acc=%s)", str);
639 PINFO (
"Cleaning up superfluous lot links in account %s\n", str);
643 lot_count = g_list_length (lots);
644 for (node = lots; node; node = node->next)
646 GNCLot *lot = node->data;
648 PINFO(
"Start processing lot %d of %d",
649 curr_lot_no + 1, lot_count);
651 if (curr_lot_no % 100 == 0)
653 char *progress_msg = g_strdup_printf (message, str, curr_lot_no, lot_count);
654 (percentagefunc)(progress_msg, (100 * curr_lot_no) / lot_count);
655 g_free (progress_msg);
661 PINFO(
"Finished processing lot %d of %d",
662 curr_lot_no + 1, lot_count);
667 (percentagefunc)(NULL, -1.0);
668 LEAVE (
"(acc=%s)", str);
677 gint split_count = 0;
680 const char *message = _(
"Checking business splits in account %s: %u of %u");
684 if (gnc_get_abort_scrub())
685 (percentagefunc)(NULL, -1.0);
690 str = str ? str :
"(null)";
692 ENTER (
"(acc=%s)", str);
693 PINFO (
"Cleaning up superfluous lot links in account %s\n", str);
699 split_count = xaccAccountGetSplitsSize (acc);
700 for (node = splits; node; node = node->next)
702 Split *split = node->data;
704 PINFO(
"Start processing split %d of %d",
705 curr_split_no + 1, split_count);
707 if (gnc_get_abort_scrub ())
710 if (curr_split_no % 100 == 0)
712 char *progress_msg = g_strdup_printf (message, str, curr_split_no, split_count);
713 (percentagefunc)(progress_msg, (100 * curr_split_no) / split_count);
714 g_free (progress_msg);
723 PINFO(
"Finished processing split %d of %d",
724 curr_split_no + 1, split_count);
727 g_list_free (splits);
729 (percentagefunc)(NULL, -1.0);
730 LEAVE (
"(acc=%s)", str);
748 lot_scrub_cb (
Account *acc, gpointer data)
759 gnc_account_foreach_descendant(acc, lot_scrub_cb, percentagefunc);
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...
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)
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)
Convenience routine to identify the earliest date in the lot.
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.
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.
Find the least common multiple of the arguments' denominators and use that as the denominator of the ...
void xaccSplitSetMemo(Split *split, const char *memo)
The memo is an arbitrary string associated with a split.
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)
Returns a list 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.
void gnc_lot_remove_split(GNCLot *lot, Split *split)
Adds a split from this 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...
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.
Split * gncOwnerFindOffsettingSplit(GNCLot *lot, gnc_numeric target_amount)
Helper function to find a split in lot that best offsets target_amount Obviously it should be of oppo...
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.
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
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
Account * gnc_lot_get_account(const GNCLot *lot)
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...
gboolean gncOwnerReduceSplitTo(Split *split, gnc_numeric target_amount)
Helper function to reduce the amount of a split to target_amount.
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.