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)); }
gboolean xaccScrubMergeSubSplits (Split *split, gboolean strict) { gboolean rc = FALSE; Transaction *txn; SplitList *node; GNCLot *lot; if (strict && (FALSE == is_subsplit (split))) return FALSE; txn = split->parent; // Don't mess with splits from an invoice transaction // Those are the responsibility of the business code if (gncInvoiceGetInvoiceFromTxn (txn)) return FALSE; lot = xaccSplitGetLot (split); ENTER ("(Lot=%s)", gnc_lot_get_title(lot)); restart: for (node = txn->splits; node; node = node->next) { Split *s = node->data; if (xaccSplitGetLot (s) != lot) continue; if (s == split) continue; if (qof_instance_get_destroying(s)) continue; // Don't mess with splits from an invoice transaction // Those are the responsibility of the business code if (gncInvoiceGetInvoiceFromTxn (s->parent)) return FALSE; if (strict) { /* OK, this split is in the same lot (and thus same account) * as the indicated split. Make sure it is really a subsplit * of the split we started with. It's possible to have two * splits in the same lot and transaction that are not subsplits * of each other, the test-period test suite does this, for * example. Only worry about adjacent sub-splits. By * repeatedly merging adjacent subsplits, we'll get the non- * adjacent ones too. */ if (!xaccSplitIsPeerSplit (split, s)) continue; } merge_splits (split, s); rc = TRUE; goto restart; } if (rc && gnc_numeric_zero_p (split->amount)) { time64 pdate = xaccTransGetDate (txn); gchar *pdatestr = gnc_ctime (&pdate); PWARN ("Result of merge has zero amt!"); PWARN ("Transaction details - posted date %s - description %s", pdatestr, xaccTransGetDescription(txn)); g_free (pdatestr); } LEAVE (" splits merged=%d", rc); return rc; }
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; }
gboolean xaccScrubMergeSubSplits (Split *split) { gboolean rc = FALSE; Transaction *txn; SplitList *node; GNCLot *lot; const GncGUID *guid; if (FALSE == is_subsplit (split)) return FALSE; txn = split->parent; lot = xaccSplitGetLot (split); ENTER ("(Lot=%s)", gnc_lot_get_title(lot)); restart: for (node = txn->splits; node; node = node->next) { Split *s = node->data; if (xaccSplitGetLot (s) != lot) continue; if (s == split) continue; if (qof_instance_get_destroying(s)) continue; /* OK, this split is in the same lot (and thus same account) * as the indicated split. Make sure it is really a subsplit * of the split we started with. It's possible to have two * splits in the same lot and transaction that are not subsplits * of each other, the test-period test suite does this, for * example. Only worry about adjacent sub-splits. By * repeatedly merging adjacent subsplits, we'll get the non- * adjacent ones too. */ guid = qof_instance_get_guid(s); if (gnc_kvp_bag_find_by_guid (split->inst.kvp_data, "lot-split", "peer_guid", guid) == NULL) continue; merge_splits (split, s); rc = TRUE; goto restart; } if (gnc_numeric_zero_p (split->amount)) { PWARN ("Result of merge has zero amt!"); } LEAVE (" splits merged=%d", rc); return rc; }
void xaccLotScrubDoubleBalance (GNCLot *lot) { gnc_commodity *currency = NULL; SplitList *snode; GList *node; gnc_numeric zero = gnc_numeric_zero(); gnc_numeric value = zero; if (!lot) return; ENTER ("lot=%s", gnc_lot_get_title(lot)); for (snode = gnc_lot_get_split_list(lot); snode; snode = snode->next) { Split *s = snode->data; xaccSplitComputeCapGains (s, NULL); } /* We double-check only closed lots */ if (FALSE == gnc_lot_is_closed (lot)) { LEAVE ("lot=%s is closed", gnc_lot_get_title(lot)); return; } for (snode = gnc_lot_get_split_list(lot); snode; snode = snode->next) { Split *s = snode->data; Transaction *trans = s->parent; /* Check to make sure all splits in the lot have a common currency */ if (NULL == currency) { currency = trans->common_currency; } if (FALSE == gnc_commodity_equiv (currency, trans->common_currency)) { /* This lot has mixed currencies. Can't double-balance. * Silently punt */ PWARN ("Lot with multiple currencies:\n" "\ttrans=%s curr=%s", xaccTransGetDescription(trans), gnc_commodity_get_fullname(trans->common_currency)); break; } /* Now, total up the values */ value = gnc_numeric_add (value, xaccSplitGetValue (s), GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT); PINFO ("Split=%p value=%s Accum Lot value=%s", s, gnc_num_dbg_to_string (s->value), gnc_num_dbg_to_string (value)); } if (FALSE == gnc_numeric_equal (value, zero)) { /* Unhandled error condition. Not sure what to do here, * Since the ComputeCapGains should have gotten it right. * I suppose there might be small rounding errors, a penny or two, * the ideal thing would to figure out why there's a rounding * error, and fix that. */ PERR ("Closed lot fails to double-balance !! lot value=%s", gnc_num_dbg_to_string (value)); for (node = gnc_lot_get_split_list(lot); node; node = node->next) { Split *s = node->data; PERR ("s=%p amt=%s val=%s", s, gnc_num_dbg_to_string(s->amount), gnc_num_dbg_to_string(s->value)); } } LEAVE ("lot=%s", gnc_lot_get_title(lot)); }