예제 #1
0
static gboolean
gncScrubLotIsSingleLotLinkSplit (GNCLot *lot)
{
    Split *split = NULL;
    Transaction *trans = NULL;

    // Lots with a single split which is also a lot link transaction split
    // may be sign of a dangling payment. Let's try to fix that

    // Only works for single split lots...
    if (1 != gnc_lot_count_splits (lot))
        return FALSE;

    split = gnc_lot_get_earliest_split (lot);
    trans = xaccSplitGetParent (split);

    if (!trans)
    {
        // Ooops - the split doesn't belong to any transaction !
        // This is not expected so issue a warning and continue with next split
        PWARN("Encountered a split in a business lot that's not part of any transaction. "
              "This is unexpected! Skipping split %p.", split);
        return FALSE;
    }

    // Only works if single split belongs to a lot link transaction...
    if (xaccTransGetTxnType (trans) != TXN_TYPE_LINK)
        return FALSE;

    return TRUE;
}
// Transaction Type
static gchar*
add_type (gchar *so_far, Transaction *trans, CsvExportInfo *info)
{
    gchar       *result;
    char         type;
    static char  ss[2];

    type = xaccTransGetTxnType (trans);

    if (type == TXN_TYPE_NONE)
        type = ' ';
    ss[0] = type;
    ss[1] = '\0';
    result = g_strconcat (so_far, ss, info->mid_sep, NULL);
    g_free (so_far);
    return result;
}
예제 #3
0
void
gncScrubBusinessSplit (Split *split)
{
    const gchar *memo = _("Please delete this transaction. Explanation at http://wiki.gnucash.org/wiki/Business_Features_Issues#Double_Posting");
    Transaction *txn;

    if (!split) return;
    ENTER ("(split=%p)", split);

    txn = xaccSplitGetParent (split);
    if (txn)
    {
        gchar txntype = xaccTransGetTxnType (txn);
        const gchar *read_only = xaccTransGetReadOnly (txn);
        gboolean is_void = xaccTransGetVoidStatus (txn);
        GNCLot *lot = xaccSplitGetLot (split);

        /* Look for transactions as a result of double posting an invoice or bill
         * Refer to https://bugzilla.gnome.org/show_bug.cgi?id=754209
         * to learn how this could have happened in the past.
         * Characteristics of such transaction are:
         * - read only
         * - not voided (to ensure read only is set by the business functions)
         * - transaction type is none (should be type invoice for proper post transactions)
         * - assigned to a lot
         */
        if ((txntype == TXN_TYPE_NONE) && read_only && !is_void && lot)
        {
            gchar *txn_date = qof_print_date (xaccTransGetDateEntered (txn));
            xaccTransClearReadOnly (txn);
            xaccSplitSetMemo (split, memo);
            gnc_lot_remove_split (lot, split);
            PWARN("Cleared double post status of transaction \"%s\", dated %s. "
                  "Please delete transaction and verify balance.",
                  xaccTransGetDescription (txn),
                  txn_date);
            g_free (txn_date);
        }

    }

    LEAVE ("(split=%p)", split);
}
예제 #4
0
/* Find an existing lot link transaction in the given lot
 * Only use a lot link that already links at least two
 * documents (to avoid perpetuating the lot link proliferation
 * that happened in 2.6.0-2.6.3).
 */
static Transaction *
get_ll_transaction_from_lot (GNCLot *lot)
{
    SplitList *ls_iter;

    /* This should really only be called on a document lot */
    if (!gncInvoiceGetInvoiceFromLot (lot))
        return NULL;

    /* The given lot is a valid document lot. Now iterate over all
     * other lot links in this lot to find one more document lot.
     */
    for (ls_iter = gnc_lot_get_split_list (lot); ls_iter; ls_iter = ls_iter->next)
    {
        Split *ls = ls_iter->data;
        Transaction *ll_txn = xaccSplitGetParent (ls);
        SplitList *ts_iter;

        if (xaccTransGetTxnType (ll_txn) != TXN_TYPE_LINK)
            continue;

        for (ts_iter = xaccTransGetSplitList (ll_txn); ts_iter; ts_iter = ts_iter->next)
        {
            Split *ts = ts_iter->data;
            GNCLot *tslot = xaccSplitGetLot (ts);

            if (!tslot)
                continue;

            if (tslot == lot)
                continue;

            if (gncInvoiceGetInvoiceFromLot (lot))
                return ll_txn; /* Got one more document lot - mission accomplished */
        }
    }

    /* The lot doesn't have an ll_txn with the requested criteria... */
    return NULL;
}
// Second Date
static gchar*
add_second_date (gchar *so_far, Transaction *trans, CsvExportInfo *info)
{
    gchar       *result;
    const gchar *second_date;
    char         type;
    Timespec     ts = {0,0};

    type = xaccTransGetTxnType (trans);

    if (type == TXN_TYPE_INVOICE)
    {
        xaccTransGetDateDueTS (trans, &ts);
        second_date = gnc_print_date (ts);
        result = g_strconcat (so_far, second_date, info->mid_sep, NULL);
    }
    else
        result = g_strconcat (so_far, info->mid_sep, NULL);

    g_free (so_far);
    return result;
}
예제 #6
0
static void
gnc_plugin_business_update_menus (GncPluginPage *plugin_page)
{
    GncMainWindow  *window;
    GtkActionGroup *action_group;
    gboolean is_txn_register, is_bus_txn = FALSE, is_bus_doc = FALSE;

    // We continue only if the current page is a plugin page
    if (!plugin_page || !GNC_IS_PLUGIN_PAGE(plugin_page))
        return;

    // Check that this is a main window and not embedded sx
    if (!GNC_IS_MAIN_WINDOW(plugin_page->window))
        return;

    is_txn_register = GNC_IS_PLUGIN_PAGE_REGISTER(plugin_page);
    window = GNC_MAIN_WINDOW(plugin_page->window);
    g_return_if_fail(GNC_IS_MAIN_WINDOW(window));
    action_group = gnc_main_window_get_action_group(window, PLUGIN_ACTIONS_NAME);
    g_return_if_fail(GTK_IS_ACTION_GROUP(action_group));

    if (is_txn_register)
    {
        Transaction *trans = gnc_plugin_page_register_get_current_txn (GNC_PLUGIN_PAGE_REGISTER(plugin_page));
        if (xaccTransCountSplits(trans) > 0)
            is_bus_txn = (xaccTransGetFirstAPARAcctSplit(trans, TRUE) != NULL);
        is_bus_doc = (xaccTransGetTxnType (trans) == TXN_TYPE_INVOICE);
    }
    // Change visibility and also sensitivity according to whether we are in a txn register
    gnc_plugin_update_actions (action_group, register_txn_actions,
                               "sensitive", is_txn_register && !is_bus_txn && !is_bus_doc);
    gnc_plugin_update_actions (action_group, register_txn_actions,
                               "visible", is_txn_register && !is_bus_txn && !is_bus_doc);
    gnc_plugin_update_actions (action_group, register_bus_txn_actions,
                               "sensitive", is_txn_register && is_bus_txn && !is_bus_doc);
    gnc_plugin_update_actions (action_group, register_bus_txn_actions,
                               "visible", is_txn_register && is_bus_txn && !is_bus_doc);
}
예제 #7
0
gboolean
gnc_tree_util_split_reg_rotate (GncTreeViewSplitReg *view, GtkTreeViewColumn *col, Transaction *trans, Split *split)
{
    GtkCellRenderer *cr0 = NULL;
    GList *renderers;
    ViewCol viewcol;

    // Get the first renderer, it has the view-column value.
    renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (col));
    cr0 = g_list_nth_data (renderers, 0);
    g_list_free (renderers);

    viewcol = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cr0), "view_column"));

    if (viewcol == COL_RECN)
    {
        const char recn_flags[] = {NREC, CREC, 0}; // List of reconciled flags
        const gchar *flags;
        const gchar *text;
        gchar *this_flag;
        gint index = 0;
        char rec;

        flags = recn_flags;

        text = g_strdup_printf("%c", xaccSplitGetReconcile (split));

        /* Find the existing text in the list of flags */
        this_flag = strstr (flags, text);

        if (this_flag != NULL && *this_flag != '\0')
        {
            /* In the list, choose the next item in the list
               (wrapping around as necessary). */
            index = this_flag - flags;

            if (flags[index + 1] != '\0')
                index = index + 1;
            else
                index = 0;

            rec = recn_flags[index];
        }
        else
            rec = NREC;

        gnc_tree_view_split_reg_set_dirty_trans (view, trans);
        if (!xaccTransIsOpen (trans))
            xaccTransBeginEdit (trans);

        xaccSplitSetReconcile (split, rec);
        return TRUE;
    }

    if (viewcol == COL_TYPE)
    {
        const char type_flags[] = {TXN_TYPE_INVOICE, TXN_TYPE_PAYMENT, 0}; // list of type flags
        const gchar *flags;
        const gchar *text;
        gchar *this_flag;
        gint index = 0;
        char type;

        flags = type_flags;

        text = g_strdup_printf("%c", xaccTransGetTxnType (trans));

        /* Find the existing text in the list of flags */
        this_flag = strstr (flags, text);

        if (this_flag != NULL && *this_flag != '\0')
        {
            /* In the list, choose the next item in the list
               (wrapping around as necessary). */
            index = this_flag - flags;

            if (flags[index + 1] != '\0')
                index = index + 1;
            else
                index = 0;

            type = type_flags[index];
        }
        else
            type = TXN_TYPE_NONE;

        gnc_tree_view_split_reg_set_dirty_trans (view, trans);
        if (!xaccTransIsOpen (trans))
            xaccTransBeginEdit (trans);

        xaccTransSetTxnType (trans, type);
        return TRUE;
    }
    return FALSE;
}
예제 #8
0
static gboolean
gncScrubLotLinks (GNCLot *scrub_lot)
{
    gboolean modified = FALSE, restart_needed = FALSE;
    SplitList *sls_iter = NULL;

scrub_start:
    restart_needed = FALSE;

    // Iterate over all splits in the lot
    for (sls_iter = gnc_lot_get_split_list (scrub_lot); sls_iter; sls_iter = sls_iter->next)
    {
        Split *sl_split = sls_iter->data;
        Transaction *ll_txn = NULL; // ll_txn = "Lot Link Transaction"
        SplitList *lts_iter = NULL;

        if (!sl_split)
            continue; // next scrub lot split

        // Only lot link transactions need to be scrubbed
        ll_txn = xaccSplitGetParent (sl_split);

        if (!ll_txn)
        {
            // Ooops - the split doesn't belong to any transaction !
            // This is not expected so issue a warning and continue with next split
            PWARN("Encountered a split in a business lot that's not part of any transaction. "
                  "This is unexpected! Skipping split %p.", sl_split);
            continue;
        }

        if (xaccTransGetTxnType (ll_txn) != TXN_TYPE_LINK)
            continue; // next scrub lot split

        // Iterate over all splits in the lot link transaction
        for (lts_iter = xaccTransGetSplitList (ll_txn); lts_iter; lts_iter = lts_iter->next)
        {
            Split *ll_txn_split = lts_iter->data; // These all refer to splits in the lot link transaction
            GNCLot *remote_lot = NULL; // lot at the other end of the lot link transaction
            gboolean sl_is_doc_lot, rl_is_doc_lot;

            if (!ll_txn_split)
                continue; // next lot link transaction split

            // Skip the split in the lot we're currently scrubbing
            if (sl_split == ll_txn_split)
                continue; // next lot link transaction split

            // Only splits of opposite signed values can be scrubbed
            if (gnc_numeric_positive_p (xaccSplitGetValue (sl_split)) ==
                    gnc_numeric_positive_p (xaccSplitGetValue (ll_txn_split)))
                continue; // next lot link transaction split

            // Find linked lot via split
            remote_lot = xaccSplitGetLot (ll_txn_split);
            if (!remote_lot)
            {
                // This is unexpected - write a warning message and skip this split
                PWARN("Encountered a Lot Link transaction with a split that's not in any lot. "
                      "This is unexpected! Skipping split %p from transaction %p.", ll_txn_split, ll_txn);
                continue;
            }

            sl_is_doc_lot = (gncInvoiceGetInvoiceFromLot (scrub_lot) != NULL);
            rl_is_doc_lot = (gncInvoiceGetInvoiceFromLot (remote_lot) != NULL);

            // Depending on the type of lots we're comparing, we need different actions
            // - Two document lots (an invoice and a credit note):
            //   Special treatment - look for all document lots linked via ll_txn
            //   and update the memo to be of more use to the uses.
            // - Two payment lots:
            //   (Part of) the link will be eliminated and instead (part of)
            //   one payment will be added to the other lot to keep the balance.
            //   If the payments are not equal in abs value part of the bigger payment
            //   will be moved to the smaller payment's lot.
            // - A document and a payment lot:
            //   (Part of) the link will be eliminated and instead (part of) the real
            //   payment will be added to the document lot to handle the payment.
            if (sl_is_doc_lot && rl_is_doc_lot)
                gncOwnerSetLotLinkMemo (ll_txn);
            else if (!sl_is_doc_lot && !rl_is_doc_lot)
            {
                gint cmp = gnc_numeric_compare (gnc_numeric_abs (xaccSplitGetValue (sl_split)),
                                                gnc_numeric_abs (xaccSplitGetValue (ll_txn_split)));
                if (cmp >= 0)
                    restart_needed = scrub_other_link (scrub_lot, sl_split, remote_lot, ll_txn_split);
                else
                    restart_needed = scrub_other_link (remote_lot, ll_txn_split, scrub_lot, sl_split);
            }
            else
            {
                GNCLot *doc_lot = sl_is_doc_lot ? scrub_lot : remote_lot;
                GNCLot *pay_lot = sl_is_doc_lot ? remote_lot : scrub_lot;
                Split *ll_doc_split = sl_is_doc_lot ? sl_split : ll_txn_split;
                Split *ll_pay_split = sl_is_doc_lot ? ll_txn_split : sl_split;
                // Ok, let's try to move a payment from pay_lot to doc_lot
                restart_needed = scrub_other_link (pay_lot, ll_pay_split, doc_lot, ll_doc_split);
            }

            // If we got here, the splits in our lot and ll_txn have been severely mixed up
            // And our iterator lists are probably no longer valid
            // So let's start over
            if (restart_needed)
            {
                modified = TRUE;
                goto scrub_start;
            }

        }
    }

    return modified;
}
예제 #9
0
void
gncOwnerSetLotLinkMemo (Transaction *ll_txn)
{
    gchar *memo_prefix = _("Offset between documents: ");
    gchar *new_memo;
    SplitList *lts_iter;
    SplitList *splits = NULL, *siter;
    GList *titles = NULL, *titer;

    if (!ll_txn)
        return;

    if (xaccTransGetTxnType (ll_txn) != TXN_TYPE_LINK)
        return;

    // Find all splits in the lot link transaction that are also in a document lot
    for (lts_iter = xaccTransGetSplitList (ll_txn); lts_iter; lts_iter = lts_iter->next)
    {
        Split *split = lts_iter->data;
        GNCLot *lot;
        GncInvoice *invoice;
        gchar *title;

        if (!split)
            continue;

        lot = xaccSplitGetLot (split);
        if (!lot)
            continue;

        invoice = gncInvoiceGetInvoiceFromLot (lot);
        if (!invoice)
            continue;

        title = g_strdup_printf ("%s %s", gncInvoiceGetTypeString (invoice), gncInvoiceGetID (invoice));

        titles = g_list_insert_sorted (titles, title, (GCompareFunc)g_strcmp0);
        splits = g_list_prepend (splits, split); // splits don't need to be sorted
    }

    if (!titles)
        return; // We didn't find document lots

    // Create the memo as we'd want it to be
    new_memo = g_strconcat (memo_prefix, titles->data, NULL);
    for (titer = titles->next; titer; titer = titer->next)
    {
        gchar *tmp_memo = g_strconcat (new_memo, " - ", titer->data, NULL);
        g_free (new_memo);
        new_memo = tmp_memo;
    }
    g_list_free_full (titles, g_free);

    // Update the memos of all the splits we found previously (if needed)
    for (siter = splits; siter; siter = siter->next)
    {
        if (g_strcmp0 (xaccSplitGetMemo (siter->data), new_memo) != 0)
            xaccSplitSetMemo (siter->data, new_memo);
    }

    g_list_free (splits);
    g_free (new_memo);
}
예제 #10
0
Split *gncOwnerFindOffsettingSplit (GNCLot *lot, gnc_numeric target_value)
{
    SplitList *ls_iter = NULL;
    Split *best_split = NULL;
    gnc_numeric best_val = { 0, 1};
    gint best_flags = 0;

    if (!lot)
        return NULL;

    for (ls_iter = gnc_lot_get_split_list (lot); ls_iter; ls_iter = ls_iter->next)
    {
        Split *split = ls_iter->data;
        Transaction *txn;
        gnc_numeric split_value;
        gint new_flags = 0;
        gint val_cmp = 0;

        if (!split)
            continue;


        txn = xaccSplitGetParent (split);
        if (!txn)
        {
            // Ooops - the split doesn't belong to any transaction !
            // This is not expected so issue a warning and continue with next split
            PWARN("Encountered a split in a payment lot that's not part of any transaction. "
                  "This is unexpected! Skipping split %p.", split);
            continue;
        }

        // Check if this split has the opposite sign of the target value we want to offset
        split_value = xaccSplitGetValue (split);
        if (gnc_numeric_positive_p (target_value) == gnc_numeric_positive_p (split_value))
            continue;

        // Ok we have found a split that potentially can offset the target value
        // Let's see if it's better than what we have found already.
        val_cmp = gnc_numeric_compare (gnc_numeric_abs (split_value),
                                       gnc_numeric_abs (target_value));
        if (val_cmp == 0)
            new_flags += is_equal;
        else if (val_cmp > 0)
            new_flags += is_more;
        else
            new_flags += is_less;

        if (xaccTransGetTxnType (txn) != TXN_TYPE_LINK)
            new_flags += is_pay_split;

        if ((new_flags >= best_flags) &&
            (gnc_numeric_compare (gnc_numeric_abs (split_value),
                                  gnc_numeric_abs (best_val)) > 0))
        {
            // The new split is a better match than what we found so far
            best_split = split;
            best_flags = new_flags;
            best_val   = split_value;
        }
    }

    return best_split;
}