static gboolean
new_tax_table_ok_cb (NewTaxTable *ntt)
{
    TaxTableWindow *ttw;
    const char *name = NULL;
    char *message;
    Account *acc;
    gnc_numeric amount;

    g_return_val_if_fail (ntt, FALSE);
    ttw = ntt->ttw;

    /* Verify that we've got real, valid data */

    /* verify the name, maybe */
    if (ntt->new_table)
    {
        name = gtk_entry_get_text (GTK_ENTRY (ntt->name_entry));
        if (name == NULL || *name == '\0')
        {
            message = _("You must provide a name for this Tax Table.");
            gnc_error_dialog (ntt->dialog, "%s", message);
            return FALSE;
        }
        if (gncTaxTableLookupByName (ttw->book, name))
        {
            message = g_strdup_printf(_(
                                          "You must provide a unique name for this Tax Table. "
                                          "Your choice \"%s\" is already in use."), name);
            gnc_error_dialog (ntt->dialog, "%s", message);
            g_free (message);
            return FALSE;
        }
    }

    /* verify the amount. Note that negative values are allowed (required for European tax rules) */
    amount = gnc_amount_edit_get_amount (GNC_AMOUNT_EDIT (ntt->amount_entry));
    if (ntt->type == GNC_AMT_TYPE_PERCENT &&
            gnc_numeric_compare (gnc_numeric_abs (amount),
                                 gnc_numeric_create (100, 1)) > 0)
    {
        message = _("Percentage amount must be between -100 and 100.");
        gnc_error_dialog (ntt->dialog, "%s", message);
        return FALSE;
    }

    /* verify the account */
    acc = gnc_tree_view_account_get_selected_account (GNC_TREE_VIEW_ACCOUNT(ntt->acct_tree));
    if (acc == NULL)
    {
        message = _("You must choose a Tax Account.");
        gnc_error_dialog (ntt->dialog, "%s", message);
        return FALSE;
    }

    gnc_suspend_gui_refresh ();

    /* Ok, it's all valid, now either change to add this thing */
    if (ntt->new_table)
    {
        GncTaxTable *table = gncTaxTableCreate (ttw->book);
        gncTaxTableBeginEdit (table);
        gncTaxTableSetName (table, name);
        /* Reset the current table */
        ttw->current_table = table;
        ntt->created_table = table;
    }
    else
        gncTaxTableBeginEdit (ttw->current_table);

    /* Create/edit the entry */
    {
        GncTaxTableEntry *entry;

        if (ntt->entry)
        {
            entry = ntt->entry;
        }
        else
        {
            entry = gncTaxTableEntryCreate ();
            gncTaxTableAddEntry (ttw->current_table, entry);
            ttw->current_entry = entry;
        }

        gncTaxTableEntrySetAccount (entry, acc);
        gncTaxTableEntrySetType (entry, ntt->type);
        gncTaxTableEntrySetAmount (entry, amount);
    }

    /* Mark the table as changed and commit it */
    gncTaxTableChanged (ttw->current_table);
    gncTaxTableCommitEdit (ttw->current_table);

    gnc_resume_gui_refresh();
    return TRUE;
}
예제 #2
0
/***********************************************************************
 * @todo Maybe invoice checking should be done in gnc_bi_import_fix_bis (...)
 * rather than in here?  But that is more concerned with ensuring the csv is consistent.
 * @param GtkListStore *store
 * @param guint *n_invoices_created
 * @param guint *n_invoices_updated
 * @return void
 ***********************************************************************/
void
gnc_bi_import_create_bis (GtkListStore * store, QofBook * book,
                          guint * n_invoices_created,
                          guint * n_invoices_updated,
                          gchar * type, gchar * open_mode, GString * info)
{
    gboolean valid;
    GtkTreeIter iter;
    gchar *id, *date_opened, *owner_id, *billing_id, *notes;
    gchar *date, *desc, *action, *account, *quantity, *price, *disc_type,
          *disc_how, *discount, *taxable, *taxincluded, *tax_table;
    gchar *date_posted, *due_date, *account_posted, *memo_posted,
          *accumulatesplits;
    guint dummy;
    GncInvoice *invoice;
    GncEntry *entry;
    gint day, month, year;
    gnc_numeric value;
    GncOwner *owner;
    Account *acc;
    enum update {YES = GTK_RESPONSE_YES, NO = GTK_RESPONSE_NO} update;
    GtkWidget *dialog;
    Timespec today;
    InvoiceWindow *iw;
    gchar *new_id = NULL;
    gint64 denom = 0;
    gnc_commodity *currency;
    
    // these arguments are needed
    g_return_if_fail (store && book);
    // logic of this function only works for bills or invoices
    g_return_if_fail ((g_ascii_strcasecmp (type, "INVOICE") == 0) ||
            (g_ascii_strcasecmp (type, "BILL") == 0));

    // allow to call this function without statistics
    if (!n_invoices_created)
        n_invoices_created = &dummy;
    if (!n_invoices_updated)
        n_invoices_updated = &dummy;
    *n_invoices_created = 0;
    *n_invoices_updated = 0;

    invoice = NULL;
    update = NO;

    valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter);
    while (valid)
    {
        // Walk through the list, reading each row
        gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
                            ID, &id,
                            DATE_OPENED, &date_opened,
                            DATE_POSTED, &date_posted,       // if autoposting requested
                            DUE_DATE, &due_date,             // if autoposting requested
                            ACCOUNT_POSTED, &account_posted, // if autoposting requested
                            MEMO_POSTED, &memo_posted,       // if autoposting requested
                            ACCU_SPLITS, &accumulatesplits,  // if autoposting requested
                            OWNER_ID, &owner_id,
                            BILLING_ID, &billing_id,
                            NOTES, &notes,
                            DATE, &date,
                            DESC, &desc,
                            ACTION, &action,
                            ACCOUNT, &account,
                            QUANTITY, &quantity,
                            PRICE, &price,
                            DISC_TYPE, &disc_type,
                            DISC_HOW, &disc_how,
                            DISCOUNT, &discount,
                            TAXABLE, &taxable,
                            TAXINCLUDED, &taxincluded,
                            TAX_TABLE, &tax_table, -1);

        // TODO:  Assign a new invoice number if one is absent.  BUT we don't want to assign a new invoice for every line!!
        // so we'd have to flag this up somehow or add an option in the import GUI.  The former implies that we make
        // an assumption about what the importer (person) wants to do.  It seems reasonable that a CSV file full of items with
        // If an invoice exists then we add to it in this current schema.
        // no predefined invoice number is a new invoice that's in need of a new number.
        // This was  not designed to satisfy the need for repeat invoices however, so maybe we need a another method for this, after all
        // It should be easier to copy an invoice with a new ID than to go through all this malarky.
        if (g_ascii_strcasecmp (type, "BILL") == 0)
            invoice = gnc_search_bill_on_id (book, id);
        else if (g_ascii_strcasecmp (type, "INVOICE") == 0)
            invoice = gnc_search_invoice_on_id (book, id);
        DEBUG( "Existing %s ID: %s\n", type, gncInvoiceGetID(invoice));

        // If the search is empty then there is no existing invoice so make a new one
        if (invoice == NULL)
        {
             DEBUG( "Creating a new : %s\n", type );
            // new invoice
            invoice = gncInvoiceCreate (book);
            /* Protect against thrashing the DB and trying to write the invoice
             * record prematurely */
            gncInvoiceBeginEdit (invoice);
            gncInvoiceSetID (invoice, id);
            owner = gncOwnerNew ();
            if (g_ascii_strcasecmp (type, "BILL") == 0)
                gncOwnerInitVendor (owner,
                                    gnc_search_vendor_on_id (book, owner_id));
            else if (g_ascii_strcasecmp (type, "INVOICE") == 0)
                gncOwnerInitCustomer (owner,
                                      gnc_search_customer_on_id (book, owner_id));
            gncInvoiceSetOwner (invoice, owner);
            gncInvoiceSetCurrency (invoice, gncOwnerGetCurrency (owner));	// Set the invoice currency based on the owner
            if (strlen (date_opened) != 0)	// If a date is specified in CSV
            {
                // FIXME: Must check for the return value of qof_scan_date!
                qof_scan_date (date_opened, &day, &month, &year);
                gncInvoiceSetDateOpened (invoice,
                                         gnc_dmy2timespec (day, month, year));
            }
            else			// If no date in CSV
            {
                time64 now = gnc_time (NULL);
                Timespec now_timespec;
                timespecFromTime64 (&now_timespec, now);
                gncInvoiceSetDateOpened (invoice, now_timespec);
            }
            gncInvoiceSetBillingID (invoice, billing_id ? billing_id : "");
            gncInvoiceSetNotes (invoice, notes ? notes : "");
            gncInvoiceSetActive (invoice, TRUE);
            //if (g_ascii_strcasecmp(type,"INVOICE"))gncInvoiceSetBillTo( invoice, billto );
            (*n_invoices_created)++;
            update = YES;

            // open new bill / invoice in a tab, if requested
            if (g_ascii_strcasecmp(open_mode, "ALL") == 0
                    || (g_ascii_strcasecmp(open_mode, "NOT_POSTED") == 0
                        && strlen(date_posted) == 0))
            {
                iw =  gnc_ui_invoice_edit (invoice);
                gnc_plugin_page_invoice_new (iw);
            }
            gncInvoiceCommitEdit (invoice);
        }
// I want to warn the user that an existing billvoice exists, but not every
// time.
// An import can contain many lines usually referring to the same invoice.
// NB: Posted invoices are NEVER updated.
        else			// if invoice exists
        {
            if (gncInvoiceIsPosted (invoice))	// Is it already posted?
            {
                valid =
                    gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);
                continue;		// If already posted then never import
            }
            if (update != YES)	// Pop up a dialog to ask if updates are the expected action
            {
                dialog = gtk_message_dialog_new (NULL,
                                                 GTK_DIALOG_MODAL,
                                                 GTK_MESSAGE_ERROR,
                                                 GTK_BUTTONS_YES_NO,
                                                 "%s",
                                                 _("Are you sure you have bills/invoices to update?"));
                update = gtk_dialog_run (GTK_DIALOG (dialog));
                gtk_widget_destroy (dialog);
                if (update == NO)
                {
                    // Cleanup and leave
                    g_free (id);
                    g_free (date_opened);
                    g_free (owner_id);
                    g_free (billing_id);
                    g_free (notes);
                    g_free (date);
                    g_free (desc);
                    g_free (action);
                    g_free (account);
                    g_free (quantity);
                    g_free (price);
                    g_free (disc_type);
                    g_free (disc_how);
                    g_free (discount);
                    g_free (taxable);
                    g_free (taxincluded);
                    g_free (tax_table);
                    g_free (date_posted);
                    g_free (due_date);
                    g_free (account_posted);
                    g_free (memo_posted);
                    g_free (accumulatesplits);
                    return;
                }
            }
            (*n_invoices_updated)++;
        }


        // add entry to invoice/bill
        entry = gncEntryCreate (book);
        gncEntryBeginEdit(entry);
        currency = gncInvoiceGetCurrency(invoice);
        if (currency) denom = gnc_commodity_get_fraction(currency);
        // FIXME: Must check for the return value of qof_scan_date!
        qof_scan_date (date, &day, &month, &year);
        {
            GDate *date = g_date_new_dmy(day, month, year);
            gncEntrySetDateGDate (entry, date);
            g_date_free (date);
        }
        timespecFromTime64 (&today, gnc_time (NULL));	// set today to the current date
        gncEntrySetDateEntered (entry, today);
        gncEntrySetDescription (entry, desc);
        gncEntrySetAction (entry, action);
        value = gnc_numeric_zero();
        gnc_exp_parser_parse (quantity, &value, NULL);
        gncEntrySetQuantity (entry, value);
        acc = gnc_account_lookup_for_register (gnc_get_current_root_account (),
                                               account);
        
        if (g_ascii_strcasecmp (type, "BILL") == 0)
        {
            gncEntrySetBillAccount (entry, acc);
            value = gnc_numeric_zero();
            gnc_exp_parser_parse (price, &value, NULL);
            gncEntrySetBillPrice (entry, value);
            gncEntrySetBillTaxable (entry, text2bool (taxable));
            gncEntrySetBillTaxIncluded (entry, text2bool (taxincluded));
            gncEntrySetBillTaxTable (entry, gncTaxTableLookupByName (book, tax_table));
            gncEntryCommitEdit(entry);
            gncBillAddEntry (invoice, entry);
        }
        else if (g_ascii_strcasecmp (type, "INVOICE") == 0)
        {
            gncEntrySetNotes (entry, notes);
            gncEntrySetInvAccount (entry, acc);
            value = gnc_numeric_zero();
            gnc_exp_parser_parse (price, &value, NULL);
            gncEntrySetInvPrice (entry, value);
            gncEntrySetInvTaxable (entry, text2bool (taxable));
            gncEntrySetInvTaxIncluded (entry, text2bool (taxincluded));
            gncEntrySetInvTaxTable (entry, gncTaxTableLookupByName (book, tax_table));
            value = gnc_numeric_zero();
            gnc_exp_parser_parse (discount, &value, NULL);
            gncEntrySetInvDiscount (entry, value);
            gncEntrySetInvDiscountType (entry, text2disc_type (disc_type));
            gncEntrySetInvDiscountHow (entry, text2disc_how (disc_how));
            gncEntryCommitEdit(entry);
            gncInvoiceAddEntry (invoice, entry);
        }
        valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);

        // handle auto posting of invoices
    
        
        if (valid)
            gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, ID, &new_id, -1);
        if (g_strcmp0 (id, new_id) != 0)
        {
            // the next invoice id is different => try to autopost this invoice
            if (qof_scan_date (date_posted, &day, &month, &year))
            {
                // autopost this invoice
                gboolean auto_pay;
                Timespec d1, d2;

                if (g_ascii_strcasecmp (type, "INVOICE") == 0)
                    auto_pay = gnc_prefs_get_bool (GNC_PREFS_GROUP_INVOICE, GNC_PREF_AUTO_PAY);
                else
                    auto_pay = gnc_prefs_get_bool (GNC_PREFS_GROUP_BILL, GNC_PREF_AUTO_PAY);

                d1 = gnc_dmy2timespec (day, month, year);
                // FIXME: Must check for the return value of qof_scan_date!
                qof_scan_date (due_date, &day, &month, &year);	// obtains the due date, or leaves it at date_posted
                d2 = gnc_dmy2timespec (day, month, year);
                acc = gnc_account_lookup_for_register
                      (gnc_get_current_root_account (), account_posted);
                gncInvoicePostToAccount (invoice, acc, &d1, &d2,
                                         memo_posted,
                                         text2bool (accumulatesplits),
                                         auto_pay);
            }

        }
        
        
    }
    // cleanup
    g_free (new_id);
    g_free (id);
    g_free (date_opened);
    g_free (owner_id);
    g_free (billing_id);
    g_free (notes);
    g_free (date);
    g_free (desc);
    g_free (action);
    g_free (account);
    g_free (quantity);
    g_free (price);
    g_free (disc_type);
    g_free (disc_how);
    g_free (discount);
    g_free (taxable);
    g_free (taxincluded);
    g_free (tax_table);
    g_free (date_posted);
    g_free (due_date);
    g_free (account_posted);
    g_free (memo_posted);
    g_free (accumulatesplits);
    
}
예제 #3
0
static gboolean gnc_entry_ledger_traverse (VirtualLocation *p_new_virt_loc,
        gncTableTraversalDir dir,
        gpointer user_data)
{
    GncEntryLedger *ledger = user_data;
    GncEntry *entry, *new_entry;
    gint response;
    VirtualLocation virt_loc;
    int changed;
    char const *cell_name;
    gboolean exact_traversal;

    if (!ledger) return FALSE;

    exact_traversal = (dir == GNC_TABLE_TRAVERSE_POINTER);

    entry = gnc_entry_ledger_get_current_entry (ledger);
    if (!entry)
        return FALSE;

    /* no changes, make sure we aren't going off the end */
    changed = gnc_table_current_cursor_changed (ledger->table, FALSE);
    if (!changed)
        return FALSE;

    virt_loc = *p_new_virt_loc;

    cell_name = gnc_table_get_current_cell_name (ledger->table);

    /* See if we are leaving the account field */
    do
    {
        ComboCell *cell;
        char *name;
        char *cell_name = NULL;

        switch (ledger->type)
        {
        case GNCENTRY_INVOICE_ENTRY:
        case GNCENTRY_INVOICE_VIEWER:
        case GNCENTRY_CUST_CREDIT_NOTE_ENTRY:
        case GNCENTRY_CUST_CREDIT_NOTE_VIEWER:
            cell_name = ENTRY_IACCT_CELL;
            break;
        case GNCENTRY_BILL_ENTRY:
        case GNCENTRY_BILL_VIEWER:
        case GNCENTRY_EXPVOUCHER_ENTRY:
        case GNCENTRY_EXPVOUCHER_VIEWER:
        case GNCENTRY_VEND_CREDIT_NOTE_ENTRY:
        case GNCENTRY_VEND_CREDIT_NOTE_VIEWER:
        case GNCENTRY_EMPL_CREDIT_NOTE_ENTRY:
        case GNCENTRY_EMPL_CREDIT_NOTE_VIEWER:
            cell_name = ENTRY_BACCT_CELL;
            break;
        default:
            g_warning ("Unhandled ledger type");
            break;
        }

        if (!cell_name)
            break;

        if (!gnc_cell_name_equal (cell_name, cell_name))
            break;

        if (!gnc_table_layout_get_cell_changed (ledger->table->layout,
                                                cell_name, FALSE))
            break;

        cell = (ComboCell *) gnc_table_layout_get_cell (ledger->table->layout,
                cell_name);
        if (!cell)
            break;

        name = cell->cell.value;
        if (!name || *name == '\0')
            break;

        /* Create the account if necessary. Also checks for a placeholder */
        if (!gnc_entry_ledger_get_account_by_name (ledger, (BasicCell *) cell,
                cell->cell.value,
                &ledger->full_refresh))
            return TRUE;

    }
    while (FALSE);


    /* See if we are leaving the TaxTable field */
    do
    {
        ComboCell *cell;
        GncTaxTable *table;
        char *name;

        if (!gnc_cell_name_equal (cell_name, ENTRY_TAXTABLE_CELL))
            break;

        if (!gnc_table_layout_get_cell_changed (ledger->table->layout,
                                                ENTRY_TAXTABLE_CELL, FALSE))
            break;

        cell = (ComboCell *) gnc_table_layout_get_cell (ledger->table->layout,
                ENTRY_TAXTABLE_CELL);
        if (!cell)
            break;

        name = cell->cell.value;
        if (!name || *name == '\0')
            break;

        table = gncTaxTableLookupByName (ledger->book, cell->cell.value);
        if (table)
            break;

        {
            const char *format = _("The tax table %s does not exist. "
                                   "Would you like to create it?");
            if (!gnc_verify_dialog (ledger->parent, TRUE, format, name))
                break;
        }

        ledger->full_refresh = FALSE;

        table = gnc_ui_tax_table_new_from_name (ledger->book, name);
        if (!table)
            break;

        ledger->full_refresh = TRUE;

        name = (char *)gncTaxTableGetName (table);
        gnc_combo_cell_set_value (cell, name);
        gnc_basic_cell_set_changed (&cell->cell, TRUE);

    }
    while (FALSE);


    /* See if we are tabbing off the end of the very last line
     * (i.e. the blank entry)
     */
    do
    {
        VirtualLocation virt_loc;

        if (!changed && !ledger->blank_entry_edited)
            break;

        if (dir != GNC_TABLE_TRAVERSE_RIGHT)
            break;

        virt_loc = ledger->table->current_cursor_loc;
        if (gnc_table_move_vertical_position (ledger->table, &virt_loc, 1))
            break;

        virt_loc = ledger->table->current_cursor_loc;
        if (gnc_table_move_tab (ledger->table, &virt_loc, TRUE))
            break;

        *p_new_virt_loc = ledger->table->current_cursor_loc;

        /* Yep, we're trying to leave the blank entry -- make sure
         * we are allowed to do so by verifying the current cursor.
         * If the current cursor is ok, then move on!
         */

        /* Verify that the cursor is ok.  If we can't save the cell, don't move! */
        if (!gnc_entry_ledger_verify_can_save (ledger))
        {
            return TRUE;
        }

        (p_new_virt_loc->vcell_loc.virt_row)++;
        p_new_virt_loc->phys_row_offset = 0;
        p_new_virt_loc->phys_col_offset = 0;

        ledger->traverse_to_new = TRUE;

        /* If we're here, we're tabbing off the end of the 'blank entry' */
        return FALSE;

    }
    while (FALSE);

    /* Now see if we are changing cursors. If not, we may be able to
     * auto-complete. */
    if (!gnc_table_virtual_cell_out_of_bounds (ledger->table,
            virt_loc.vcell_loc))
    {
        if (gnc_entry_ledger_auto_completion (ledger, dir, p_new_virt_loc))
            return FALSE;
    }

    /* Check for going off the end */
    gnc_table_find_close_valid_cell (ledger->table, &virt_loc, exact_traversal);

    /* Same entry, no problem -- we're just moving backwards in the cursor */
    new_entry = gnc_entry_ledger_get_entry (ledger, virt_loc.vcell_loc);
    if (entry == new_entry)
    {
        *p_new_virt_loc = virt_loc;

        return FALSE;
    }

    /* If we are here, then we are trying to leave the cursor.  Make sure
     * the cursor we are leaving is valid.  If so, ask the user if the
     * changes should be recorded.  If not, don't go anywhere.
     */

    /* Verify this cursor -- if it's not valid, don't let them move on */
    if (!gnc_entry_ledger_verify_can_save (ledger))
    {
        *p_new_virt_loc = ledger->table->current_cursor_loc;
        return TRUE;
    }

    /*
     * XXX  GNCENTRY_INVOICE_EDIT processing to be added:
     * 1) check if the qty field changed.
     * 2) if so, check if this entry is part of an order.
     * 3) if so, ask if they want to change the entry or
     *    split the entry into two parts.
     */

    /* Ok, we are changing lines and the current entry has
     * changed. We only ask them what they want to do in
     * limited cases -- usually just let the change go through.
     */
    {
        GtkWidget *dialog;
        const char *title = _("Save the current entry?");
        const char *message =
            _("The current entry has been changed.  However, this entry is "
              "part of an existing order. Would you like to record the change "
              "and effectively change your order?");

        switch (ledger->type)
        {
        case GNCENTRY_INVOICE_ENTRY:
        case GNCENTRY_CUST_CREDIT_NOTE_ENTRY:
            if (gncEntryGetOrder (entry) != NULL)
            {
                dialog = gtk_message_dialog_new(GTK_WINDOW(ledger->parent),
                                                GTK_DIALOG_DESTROY_WITH_PARENT,
                                                GTK_MESSAGE_QUESTION,
                                                GTK_BUTTONS_NONE,
                                                "%s", title);
                gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
                        "%s", message);
                gtk_dialog_add_buttons(GTK_DIALOG(dialog),
                                       _("_Don't Record"), GTK_RESPONSE_REJECT,
                                       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                                       _("_Record"), GTK_RESPONSE_ACCEPT,
                                       NULL);
                response = gnc_dialog_run(GTK_DIALOG(dialog), "invoice_entry_changed");
                gtk_widget_destroy(dialog);
                break;
            }

            /* FALL THROUGH */
        default:
            response = GTK_RESPONSE_ACCEPT;
            break;
        }
    }

    switch (response)
    {
    case GTK_RESPONSE_ACCEPT:
        break;

    case GTK_RESPONSE_REJECT:
    {
        VirtualCellLocation vcell_loc;
        GncEntry *new_entry;

        new_entry = gnc_entry_ledger_get_entry (ledger, virt_loc.vcell_loc);

        gnc_entry_ledger_cancel_cursor_changes (ledger);

        if (gnc_entry_ledger_find_entry (ledger, new_entry, &vcell_loc))
            virt_loc.vcell_loc = vcell_loc;

        gnc_table_find_close_valid_cell (ledger->table, &virt_loc,
                                         exact_traversal);

        *p_new_virt_loc = virt_loc;
    }

    break;

    case GTK_RESPONSE_CANCEL:
    default:
        return TRUE;
    }

    return FALSE;
}