GNCLot* dom_tree_to_lot (xmlNodePtr node, QofBook *book) { struct lot_pdata pdata; GNCLot *lot; gboolean successful; lot = gnc_lot_new(book); ENTER("(lot=%p)", lot); pdata.lot = lot; pdata.book = book; successful = dom_tree_generic_parse (node, lot_handlers_v2, &pdata); if (!successful) { PERR ("failed to parse lot"); gnc_lot_destroy (lot); lot = NULL; } LEAVE(""); return lot; }
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; }
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); } }