static xmlNodePtr ttentry_dom_tree_create (GncTaxTableEntry *entry) { xmlNodePtr ret; Account *account; gnc_numeric amount; ret = xmlNewNode(NULL, BAD_CAST gnc_taxtableentry_string); account = gncTaxTableEntryGetAccount (entry); if (account) xmlAddChild(ret, guid_to_dom_tree (ttentry_account_string, qof_instance_get_guid (QOF_INSTANCE(account)))); amount = gncTaxTableEntryGetAmount (entry); xmlAddChild (ret, gnc_numeric_to_dom_tree (ttentry_amount_string, &amount)); xmlAddChild(ret, text_to_dom_tree (ttentry_type_string, gncAmountTypeToString ( gncTaxTableEntryGetType (entry)))); return ret; }
/* * This is the logic of computing the total for an Entry, so you know * what values to put into various Splits or to display in the ledger. * In other words, we combine the quantity, unit-price, discount and * taxes together, depending on various flags. * * There are four potental ways to combine these numbers: * Discount: Pre-Tax Post-Tax * Tax : Included Not-Included * * The process is relatively simple: * * 1) compute the agregate price (price*qty) * 2) if taxincluded, then back-compute the agregate pre-tax price * 3) apply discount and taxes in the appropriate order * 4) return the requested results. * * step 2 can be done with agregate taxes; no need to compute them all * unless the caller asked for the tax_value. * * Note that the returned "value" is such that value + tax == "total * to pay," which means in the case of tax-included that the returned * "value" may be less than the agregate price, even without a * discount. If you want to display the tax-included value, you need * to add the value and taxes together. In other words, the value is * the amount the merchant gets; the taxes are the amount the gov't * gets, and the customer pays the sum or value + taxes. * * The SCU is the denominator to convert the value. * * The discount return value is just for entertainment -- you may want * to let a consumer know how much they saved. */ void gncEntryComputeValue (gnc_numeric qty, gnc_numeric price, const GncTaxTable *tax_table, gboolean tax_included, gnc_numeric discount, GncAmountType discount_type, GncDiscountHow discount_how, int SCU, gnc_numeric *value, gnc_numeric *discount_value, GList **tax_value) { gnc_numeric aggregate; gnc_numeric pretax; gnc_numeric result; gnc_numeric tax; gnc_numeric percent = gnc_numeric_create (100, 1); gnc_numeric tpercent = gnc_numeric_zero (); gnc_numeric tvalue = gnc_numeric_zero (); GList * entries = gncTaxTableGetEntries (tax_table); GList * node; /* Step 1: compute the aggregate price */ aggregate = gnc_numeric_mul (qty, price, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); /* Step 2: compute the pre-tax aggregate */ /* First, compute the aggregate tpercent and tvalue numbers */ for (node = entries; node; node = node->next) { GncTaxTableEntry *entry = node->data; gnc_numeric amount = gncTaxTableEntryGetAmount (entry); switch (gncTaxTableEntryGetType (entry)) { case GNC_AMT_TYPE_VALUE: tvalue = gnc_numeric_add (tvalue, amount, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); break; case GNC_AMT_TYPE_PERCENT: tpercent = gnc_numeric_add (tpercent, amount, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); break; default: g_warning ("Unknown tax type: %d", gncTaxTableEntryGetType (entry)); } } /* now we need to convert from 5% -> .05 */ tpercent = gnc_numeric_div (tpercent, percent, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); /* Next, actually compute the pre-tax aggregate value based on the * taxincluded flag. */ if (tax_table && tax_included) { /* Back-compute the pre-tax aggregate value. * We know that aggregate = pretax + pretax*tpercent + tvalue, so * pretax = (aggregate-tvalue)/(1+tpercent) */ pretax = gnc_numeric_sub (aggregate, tvalue, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); pretax = gnc_numeric_div (pretax, gnc_numeric_add (tpercent, gnc_numeric_create (1, 1), GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD), GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); } else { pretax = aggregate; } /* Step 3: apply discount and taxes in the appropriate order */ /* * There are two ways to apply discounts and taxes. In one way, you * always compute the discount off the pretax number, and compute * the taxes off of either the pretax value or "pretax-discount" * value. In the other way, you always compute the tax on "pretax", * and compute the discount on either "pretax" or "pretax+taxes". * * I don't know which is the "correct" way. */ /* * Type: discount tax * PRETAX pretax pretax-discount * SAMETIME pretax pretax * POSTTAX pretax+tax pretax */ switch (discount_how) { case GNC_DISC_PRETAX: case GNC_DISC_SAMETIME: /* compute the discount from pretax */ if (discount_type == GNC_AMT_TYPE_PERCENT) { discount = gnc_numeric_div (discount, percent, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); discount = gnc_numeric_mul (pretax, discount, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); } result = gnc_numeric_sub (pretax, discount, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); /* Figure out when to apply the tax, pretax or pretax-discount */ if (discount_how == GNC_DISC_PRETAX) pretax = result; break; case GNC_DISC_POSTTAX: /* compute discount on pretax+taxes */ if (discount_type == GNC_AMT_TYPE_PERCENT) { gnc_numeric after_tax; tax = gnc_numeric_mul (pretax, tpercent, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); after_tax = gnc_numeric_add (pretax, tax, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); after_tax = gnc_numeric_add (after_tax, tvalue, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); discount = gnc_numeric_div (discount, percent, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); discount = gnc_numeric_mul (after_tax, discount, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); } result = gnc_numeric_sub (pretax, discount, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); break; default: g_warning ("unknown DiscountHow value: %d", discount_how); } /* Step 4: return the requested results. */ /* result == amount merchant gets * discount == amount of discount * need to compute taxes (based on 'pretax') if the caller wants it. */ if (discount_value != NULL) { if (SCU) discount = gnc_numeric_convert(discount, SCU, GNC_HOW_RND_ROUND); *discount_value = discount; } if (value != NULL) { if (SCU) result = gnc_numeric_convert(result, SCU, GNC_HOW_RND_ROUND); *value = result; } /* Now... Compute the list of tax values (if the caller wants it) */ if (tax_value != NULL) { GList * taxes = NULL; for (node = entries; node; node = node->next) { GncTaxTableEntry *entry = node->data; Account *acc = gncTaxTableEntryGetAccount (entry); gnc_numeric amount = gncTaxTableEntryGetAmount (entry); g_return_if_fail (acc); switch (gncTaxTableEntryGetType (entry)) { case GNC_AMT_TYPE_VALUE: if (SCU) amount = gnc_numeric_convert(amount, SCU, GNC_HOW_RND_ROUND); taxes = gncAccountValueAdd (taxes, acc, amount); break; case GNC_AMT_TYPE_PERCENT: amount = gnc_numeric_div (amount, percent, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); tax = gnc_numeric_mul (pretax, amount, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); if (SCU) tax = gnc_numeric_convert(tax, SCU, GNC_HOW_RND_ROUND); taxes = gncAccountValueAdd (taxes, acc, tax); break; default: break; } } *tax_value = taxes; } return; }
static void tax_table_entries_refresh (TaxTableWindow *ttw) { GList *list, *node; GtkTreeView *view; GtkListStore *store; GtkTreeIter iter; GtkTreePath *path; GtkTreeSelection *selection; GtkTreeRowReference *reference = NULL; GncTaxTableEntry *selected_entry; g_return_if_fail (ttw); view = GTK_TREE_VIEW (ttw->entries_view); store = GTK_LIST_STORE(gtk_tree_view_get_model(view)); /* Clear the list */ selected_entry = ttw->current_entry; gtk_list_store_clear (store); if (ttw->current_table == NULL) return; /* Add the items to the list */ list = gncTaxTableGetEntries (ttw->current_table); if (list) list = g_list_reverse (g_list_copy (list)); for (node = list ; node; node = node->next) { char *row_text[3]; GncTaxTableEntry *entry = node->data; Account *acc = gncTaxTableEntryGetAccount (entry); gnc_numeric amount = gncTaxTableEntryGetAmount (entry); row_text[0] = gnc_account_get_full_name (acc); switch (gncTaxTableEntryGetType (entry)) { case GNC_AMT_TYPE_PERCENT: row_text[1] = g_strdup_printf ("%s%%", xaccPrintAmount (amount, gnc_default_print_info (FALSE))); break; default: row_text[1] = g_strdup_printf ("%s", xaccPrintAmount (amount, gnc_default_print_info (TRUE))); break; } gtk_list_store_prepend(store, &iter); gtk_list_store_set(store, &iter, TAX_ENTRY_COL_NAME, row_text[0], TAX_ENTRY_COL_POINTER, entry, TAX_ENTRY_COL_AMOUNT, row_text[1], -1); if (entry == selected_entry) { path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter); reference = gtk_tree_row_reference_new(GTK_TREE_MODEL(store), path); gtk_tree_path_free(path); } g_free (row_text[0]); g_free (row_text[1]); } if (reference) { path = gtk_tree_row_reference_get_path(reference); gtk_tree_row_reference_free(reference); if (path) { selection = gtk_tree_view_get_selection(view); gtk_tree_selection_select_path(selection, path); gtk_tree_view_scroll_to_cell(view, path, NULL, TRUE, 0.5, 0.0); gtk_tree_path_free(path); } } }
static GncTaxTable * new_tax_table_dialog (TaxTableWindow *ttw, gboolean new_table, GncTaxTableEntry *entry, const char *name) { GncTaxTable *created_table = NULL; NewTaxTable *ntt; GtkBuilder *builder; GtkWidget *box, *widget, *combo; gboolean done; gint response, index; if (!ttw) return NULL; if (new_table && entry) return NULL; ntt = g_new0 (NewTaxTable, 1); ntt->ttw = ttw; ntt->entry = entry; ntt->new_table = new_table; if (entry) ntt->type = gncTaxTableEntryGetType (entry); else ntt->type = GNC_AMT_TYPE_PERCENT; /* Open and read the Glade File */ builder = gtk_builder_new(); gnc_builder_add_from_file (builder, "dialog-tax-table.glade", "type_liststore"); gnc_builder_add_from_file (builder, "dialog-tax-table.glade", "New Tax Table Dialog"); ntt->dialog = GTK_WIDGET(gtk_builder_get_object (builder, "New Tax Table Dialog")); ntt->name_entry = GTK_WIDGET(gtk_builder_get_object (builder, "name_entry")); if (name) gtk_entry_set_text (GTK_ENTRY (ntt->name_entry), name); /* Create the menu */ combo = GTK_WIDGET(gtk_builder_get_object (builder, "type_combobox")); index = ntt->type ? ntt->type : GNC_AMT_TYPE_VALUE; gtk_combo_box_set_active(GTK_COMBO_BOX(combo), index - 1); g_signal_connect (combo, "changed", G_CALLBACK (combo_changed), ntt); /* Attach our own widgets */ box = GTK_WIDGET(gtk_builder_get_object (builder, "amount_box")); ntt->amount_entry = widget = gnc_amount_edit_new (); gnc_amount_edit_set_evaluate_on_enter (GNC_AMOUNT_EDIT (widget), TRUE); gnc_amount_edit_set_fraction (GNC_AMOUNT_EDIT (widget), 100000); gtk_box_pack_start (GTK_BOX (box), widget, TRUE, TRUE, 0); box = GTK_WIDGET(gtk_builder_get_object (builder, "acct_window")); ntt->acct_tree = GTK_WIDGET(gnc_tree_view_account_new (FALSE)); gtk_container_add (GTK_CONTAINER (box), ntt->acct_tree); gtk_tree_view_set_headers_visible (GTK_TREE_VIEW(ntt->acct_tree), FALSE); /* Make 'enter' do the right thing */ gtk_entry_set_activates_default(GTK_ENTRY (gnc_amount_edit_gtk_entry (GNC_AMOUNT_EDIT (ntt->amount_entry))), TRUE); /* Fix mnemonics for generated target widgets */ widget = GTK_WIDGET(gtk_builder_get_object (builder, "value_label")); gtk_label_set_mnemonic_widget (GTK_LABEL (widget), ntt->amount_entry); widget = GTK_WIDGET(gtk_builder_get_object (builder, "account_label")); gtk_label_set_mnemonic_widget (GTK_LABEL (widget), ntt->acct_tree); /* Fill in the widgets appropriately */ if (entry) { gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT (ntt->amount_entry), gncTaxTableEntryGetAmount (entry)); gnc_tree_view_account_set_selected_account (GNC_TREE_VIEW_ACCOUNT (ntt->acct_tree), gncTaxTableEntryGetAccount (entry)); } /* Set our parent */ gtk_window_set_transient_for (GTK_WINDOW(ntt->dialog), GTK_WINDOW(ttw->dialog)); /* Setup signals */ gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, ntt); /* Show what we should */ gtk_widget_show_all (ntt->dialog); if (new_table == FALSE) { gtk_widget_hide (GTK_WIDGET(gtk_builder_get_object (builder, "table_title"))); gtk_widget_hide (GTK_WIDGET(gtk_builder_get_object (builder, "table_name"))); gtk_widget_hide (GTK_WIDGET(gtk_builder_get_object (builder, "spacer"))); gtk_widget_hide (ntt->name_entry); /* Tables are great for layout, but a pain when you hide widgets */ widget = GTK_WIDGET(gtk_builder_get_object (builder, "ttd_table")); gtk_table_set_row_spacing (GTK_TABLE(widget), 0, 0); gtk_table_set_row_spacing (GTK_TABLE(widget), 1, 0); gtk_table_set_row_spacing (GTK_TABLE(widget), 2, 0); gtk_widget_grab_focus (gnc_amount_edit_gtk_entry (GNC_AMOUNT_EDIT (ntt->amount_entry))); } else gtk_widget_grab_focus (ntt->name_entry); /* Display the dialog now that we're done manipulating it */ gtk_widget_show (ntt->dialog); done = FALSE; while (!done) { response = gtk_dialog_run (GTK_DIALOG (ntt->dialog)); switch (response) { case GTK_RESPONSE_OK: if (new_tax_table_ok_cb (ntt)) { created_table = ntt->created_table; done = TRUE; } break; default: done = TRUE; break; } } g_object_unref(G_OBJECT(builder)); gtk_widget_destroy(ntt->dialog); g_free(ntt); return created_table; }