r16480 - gnucash/branches/csv-import/src/import-export/csv - Added support for a real "Balance" column;

Benjamin Sperisen lasindi at cvs.gnucash.org
Thu Aug 30 00:55:37 EDT 2007


Author: lasindi
Date: 2007-08-30 00:55:36 -0400 (Thu, 30 Aug 2007)
New Revision: 16480
Trac: http://svn.gnucash.org/trac/changeset/16480

Modified:
   gnucash/branches/csv-import/src/import-export/csv/gnc-csv-import.c
   gnucash/branches/csv-import/src/import-export/csv/gnc-csv-model.c
   gnucash/branches/csv-import/src/import-export/csv/gnc-csv-model.h
Log:
Added support for a real "Balance" column;
added specific error messages for each row on what error(s) the row had;
a few bug fixes;
added comments;
some refactoring of functions.
Transactions to be imported are also now sorted by date instead of line number.



Modified: gnucash/branches/csv-import/src/import-export/csv/gnc-csv-import.c
===================================================================
--- gnucash/branches/csv-import/src/import-export/csv/gnc-csv-import.c	2007-08-29 22:16:59 UTC (rev 16479)
+++ gnucash/branches/csv-import/src/import-export/csv/gnc-csv-import.c	2007-08-30 04:55:36 UTC (rev 16480)
@@ -68,7 +68,7 @@
   GtkEntry* custom_entry; /**< The entry for custom separators */
   gboolean encoding_selected_called; /**< Before encoding_selected is first called, this is FALSE.
                                       * (See description of encoding_selected.) */
-  /* TODO We might not need previewing_errors. */
+  gboolean not_empty; /**< FALSE initially, true after the first type gnc_csv_preview_update is called. */
   gboolean previewing_errors; /**< TRUE if the dialog is displaying
                                * error lines, instead of all the file
                                * data. */
@@ -84,7 +84,7 @@
                          * the user has clicked */
 } GncCsvPreview;
 
-static void gnc_csv_preview_update(GncCsvPreview* preview, gboolean notEmpty);
+static void gnc_csv_preview_update(GncCsvPreview* preview);
 
 /** Event handler for separator changes. This function is called
  * whenever one of the widgets for configuring the separators (the
@@ -144,13 +144,18 @@
   }
 
   /* If we parsed successfully, redisplay the data. */
-  gnc_csv_preview_update(preview, TRUE);
+  gnc_csv_preview_update(preview);
 }
 
-/* TODO Comment */
+/** Event handler for clicking one of the format type radio
+ * buttons. This occurs if the format (Fixed-Width or CSV) is changed.
+ * @param csv_button The "Separated" radio button
+ * @param preview The display of the data being imported
+ */
 static void separated_or_fixed_selected(GtkToggleButton* csv_button, GncCsvPreview* preview)
 {
   GError* error = NULL;
+  /* Set the parsing type correctly. */
   if(gtk_toggle_button_get_active(csv_button)) /* If we're in CSV mode ... */
   {
     stf_parse_options_set_type(preview->parse_data->options, PARSE_TYPE_CSV);
@@ -159,12 +164,17 @@
   {
     stf_parse_options_set_type(preview->parse_data->options, PARSE_TYPE_FIXED);
   }
+
+  /* Reparse the data. */
   if(gnc_csv_parse(preview->parse_data, FALSE, &error))
   {
+    /* Show an error dialog explaining the problem. */
     gnc_error_dialog(NULL, "%s", error->message);
     return;
   }
-  gnc_csv_preview_update(preview, TRUE);
+
+  /* Show the new data. */
+  gnc_csv_preview_update(preview);
 }
 
 /** Event handler for a new encoding. This is called when the user
@@ -172,7 +182,7 @@
  * user.
  * @param selector The widget the user uses to select a new encoding
  * @param encoding The encoding that the user selected
- * @param preview The data that is being configured
+ * @param preview The display of the data being imported
  */
 static void encoding_selected(GOCharmapSel* selector, const char* encoding,
                               GncCsvPreview* preview)
@@ -204,7 +214,7 @@
       return;
     }
 
-    gnc_csv_preview_update(preview, TRUE);
+    gnc_csv_preview_update(preview);
     preview->encoding_selected_called = FALSE;
   }
   else /* If this is the first call of the function ... */
@@ -215,7 +225,7 @@
 
 /** Event handler for selecting a new date format.
  * @param format_selector The combo box for selecting date formats
- * @param preview The data that is being configured
+ * @param preview The display of the data being imported
  */
 static void date_format_selected(GtkComboBox* format_selector, GncCsvPreview* preview)
 {
@@ -226,7 +236,7 @@
  * function updates the parse data with the user's column type
  * configuration and closes the preview dialog.
  * @param widget The "OK" button
- * @param preview The data that is being configured
+ * @param preview The display of the data being imported
  */
 static void ok_button_clicked(GtkWidget* widget, GncCsvPreview* preview)
 {
@@ -271,7 +281,7 @@
 /** Event handler for the "Cancel" button. When the user clicks
  * "Cancel", the dialog is simply closed.
  * @param widget The "Cancel" button
- * @param preview The data that is being configured
+ * @param preview The display of the data being imported
  */
 static void cancel_button_clicked(GtkWidget* widget, GncCsvPreview* preview)
 {
@@ -283,7 +293,7 @@
  * match.
  * @param widget The data treeview
  * @param allocation The size of the data treeview
- * @param preview The data that is being configured
+ * @param preview The display of the data being imported
  */
 static void treeview_resized(GtkWidget* widget, GtkAllocation* allocation, GncCsvPreview* preview)
 {
@@ -315,7 +325,7 @@
  * @param renderer The renderer of the column the user changed
  * @param path There is only 1 row in preview->ctreeview, so this is always 0.
  * @param new_text The text the user selected
- * @param preview The data that is being configured
+ * @param preview The display of the data being imported
  */
 static void column_type_edited(GtkCellRenderer* renderer, gchar* path,
                                gchar* new_text, GncCsvPreview* preview)
@@ -474,6 +484,9 @@
    * set it initially to FALSE. */
   preview->encoding_selected_called = FALSE;
 
+  /* It is empty at first. */
+  preview->not_empty = FALSE;
+
   return preview;
 }
 
@@ -488,7 +501,7 @@
 }
 
 /** Returns the cell renderer from a column in the preview's treeview.
- * @param preview The data that is being configured
+ * @param preview The display of the data being imported
  * @param col The number of the column whose cell renderer is being retrieved
  * @return The cell renderer of column number col
  */
@@ -584,7 +597,7 @@
                   gnc_error_dialog(NULL, "%s", error->message);
                   return FALSE;
                 }
-		gnc_csv_preview_update (preview, TRUE);
+		gnc_csv_preview_update (preview);
 	}
 
 	return TRUE;
@@ -618,7 +631,7 @@
                   gnc_error_dialog(NULL, "%s", error->message);
                   return FALSE;
                 }
-		gnc_csv_preview_update (preview, TRUE);
+		gnc_csv_preview_update (preview);
 	}
 	return TRUE;
 }
@@ -649,7 +662,7 @@
                   gnc_error_dialog(NULL, "%s", error->message);
                   return FALSE;
                 }
-		gnc_csv_preview_update (preview, TRUE);
+		gnc_csv_preview_update (preview);
 	}
 	return TRUE;
 }
@@ -670,7 +683,7 @@
                   gnc_error_dialog(NULL, "%s", error->message);
                   return FALSE;
                 }
-		gnc_csv_preview_update (preview, TRUE);
+		gnc_csv_preview_update (preview);
 	}
 	return TRUE;
 }
@@ -753,8 +766,6 @@
  * @param event The event that happened (where the user clicked)
  * @param preview The data being configured
  */
-/* TODO Comment */
-/* TODO Clean up */
 static void header_button_press_handler(GtkWidget* button, GdkEventButton* event,
                                         GncCsvPreview* preview)
 {
@@ -762,6 +773,7 @@
      to correct for the indentation of button. */
   int i, col = 0, offset = GTK_BIN(button)->child->allocation.x - button->allocation.x,
     ncols = preview->parse_data->column_types->len;
+  /* Find the column that was clicked. */
   for(i = 0; i < ncols; i++)
   {
     if(preview->treeview_buttons[i] == button)
@@ -777,10 +789,12 @@
     return;
   }
 
+  /* Double clicks can split columns. */
   if(event->type == GDK_2BUTTON_PRESS && event->button == 1)
   {
     make_new_column(preview, col, (int)event->x - offset, FALSE);
   }
+  /* Right clicking brings up a context menu. */
   else if(event->type == GDK_BUTTON_PRESS && event->button == 3)
   {
     fixed_context_menu(preview, event, col, (int)event->x - offset);
@@ -793,8 +807,7 @@
  * @param preview The data being previewed
  * @param notEmpty Whether this function has been called before or not
  */
-/* TODO Look at getting rid of notEmpty */
-static void gnc_csv_preview_update(GncCsvPreview* preview, gboolean notEmpty)
+static void gnc_csv_preview_update(GncCsvPreview* preview)
 {
   /* store has the data from the file being imported. cstores is an
    * array of stores that hold the combo box entries for each
@@ -836,7 +849,7 @@
     }
   }
 
-  if(notEmpty)
+  if(preview->not_empty)
   {
     GList *children, *children_begin;
     GList *tv_columns, *tv_columns_begin, *ctv_columns, *ctv_columns_begin;
@@ -970,6 +983,9 @@
   /* Set the date format to what's in the combo box (since we don't
    * necessarily know if this will always be the same). */
   preview->parse_data->date_format = gtk_combo_box_get_active(preview->date_format_combo);
+
+  /* It's now been filled with some stuff. */
+  preview->not_empty = TRUE;
 }
 
 /** A function that lets the user preview a file's data. This function
@@ -990,7 +1006,7 @@
   /* Load the data into the treeview. (This is the first time we've
    * called gnc_csv_preview_update on this preview, so we use
    * FALSE.) */
-  gnc_csv_preview_update(preview, FALSE);
+  gnc_csv_preview_update(preview);
   /* Wait until the user clicks "OK" or "Cancel". */
   gtk_dialog_run(GTK_DIALOG(preview->dialog));
 
@@ -1023,12 +1039,11 @@
   gtk_widget_show(GTK_WIDGET(instructions_image));
   gtk_widget_show(GTK_WIDGET(instructions_label));
 
-  /* TODO Implement */
   preview->previewing_errors = TRUE;
   preview->approved = FALSE; /* This is FALSE until the user clicks "OK". */
 
   /* Wait until the user clicks "OK" or "Cancel". */
-  gnc_csv_preview_update(preview, TRUE);
+  gnc_csv_preview_update(preview);
 
   /* Set the last column to have the header "Errors" so that the user
    * doesn't find the extra column confusing. */
@@ -1119,7 +1134,7 @@
     }
 
     /* Create transactions from the parsed data. */
-    gnc_parse_to_trans(parse_data, account, FALSE);
+    gnc_csv_parse_to_trans(parse_data, account, FALSE);
 
     /* If there are errors, let the user try and eliminate them by
      * previewing them. Repeat until either there are no errors or the
@@ -1127,7 +1142,7 @@
     while(!((parse_data->error_lines == NULL) || user_canceled))
     {
       user_canceled = gnc_csv_preview_errors(preview);
-      gnc_parse_to_trans(parse_data, account, TRUE);
+      gnc_csv_parse_to_trans(parse_data, account, TRUE);
     }
 
     /* Create the genereic transaction importer GUI. */

Modified: gnucash/branches/csv-import/src/import-export/csv/gnc-csv-model.c
===================================================================
--- gnucash/branches/csv-import/src/import-export/csv/gnc-csv-model.c	2007-08-29 22:16:59 UTC (rev 16479)
+++ gnucash/branches/csv-import/src/import-export/csv/gnc-csv-model.c	2007-08-30 04:55:36 UTC (rev 16480)
@@ -33,7 +33,8 @@
                                                           N_("Description"),
                                                           N_("Balance"),
                                                           N_("Deposit"),
-                                                          N_("Withdrawal")};
+                                                          N_("Withdrawal"),
+                                                          N_("Num")};
 
 /** A set of sensible defaults for parsing CSV files. 
  * @return StfParseOptions_t* for parsing a file with comma separators
@@ -470,7 +471,6 @@
  * @error error Will contain an error if there is a failure
  * @return 0 on success, 1 on failure
  */
-/* TODO Should we use 0 for domain and code in errors? */
 int gnc_csv_parse(GncCsvParseData* parse_data, gboolean guessColTypes, GError** error)
 {
   /* max_cols is the number of columns in the row with the most columns. */
@@ -498,6 +498,7 @@
   /* Record the original row lengths of parse_data->orig_lines. */
   if(parse_data->orig_row_lengths != NULL)
     g_array_free(parse_data->orig_row_lengths, FALSE);
+
   parse_data->orig_row_lengths =
     g_array_sized_new(FALSE, FALSE, sizeof(int), parse_data->orig_lines->len);
   g_array_set_size(parse_data->orig_row_lengths, parse_data->orig_lines->len);
@@ -628,6 +629,7 @@
     return *((time_t*)(prop->value)) != -1;
 
   case GNC_CSV_DESCRIPTION:
+  case GNC_CSV_NUM:
     prop->value = g_strdup(str);
     return TRUE;
 
@@ -673,9 +675,9 @@
     if(abs(value) > 0.00001)
     {
       prop->value = g_new(gnc_numeric, 1);
-      *((gnc_numeric*)(prop->value)) = double_to_gnc_numeric(value,
-                                                             xaccAccountGetCommoditySCU(prop->list->account),
-                                                             GNC_RND_ROUND);
+      *((gnc_numeric*)(prop->value)) =
+        double_to_gnc_numeric(value, xaccAccountGetCommoditySCU(prop->list->account),
+                              GNC_RND_ROUND);
     }
     return TRUE;
   }
@@ -723,8 +725,6 @@
 }
 
 /** Adds a split to a transaction.
- * This function is only called by trans_property_list_to_trans. This is in a
- * separate function to avoid copying code twice in trans_property_list_to_trans.
  * @param trans The transaction to add a split to
  * @param account The account used for the split
  * @param book The book where the split should be stored
@@ -834,26 +834,41 @@
 /** Create a Transaction from a TransPropertyList.
  * @param list The list of properties
  * @param error Contains an error on failure
- * @return On success, a pointer to a new transaction; on failure, NULL
+ * @return On success, a GncCsvTransLine; on failure, the trans pointer is NULL
  */
-/* TODO Comment */
-static Transaction* trans_property_list_to_trans(TransPropertyList* list, gchar** error)
+static GncCsvTransLine* trans_property_list_to_trans(TransPropertyList* list, gchar** error)
 {
-  Transaction* trans;
+  GncCsvTransLine* trans_line = g_new(GncCsvTransLine, 1);
   GList* properties_begin = list->properties;
   GNCBook* book = gnc_account_get_book(list->account);
   gnc_commodity* currency = xaccAccountGetCommodity(list->account);
-  gnc_numeric amount;
-  gboolean noAmountYet = TRUE;
-  
+  gnc_numeric amount = double_to_gnc_numeric(0.0, xaccAccountGetCommoditySCU(list->account),
+                                             GNC_RND_ROUND);
+
+  /* This flag is set to TRUE if we can use the "Deposit" or "Withdrawal" column. */
+  gboolean amount_set = FALSE;
+
+  /* The balance is 0 by default. */
+  trans_line->balance_set = FALSE;
+  trans_line->balance = amount;
+
+  /* We make the line_no -1 just to mark that it hasn't been set. We
+   * may get rid of line_no soon anyway, so it's not particularly
+   * important. */
+  trans_line->line_no = -1;
+
+  /* Make sure this is a transaction with all the columns we need. */
   if(!trans_property_list_verify_essentials(list, error))
+  {
+    g_free(trans_line);
     return NULL;
+  }
 
-  trans = xaccMallocTransaction(book);
-  
-  xaccTransBeginEdit(trans);
-  xaccTransSetCurrency(trans, currency);
+  trans_line->trans = xaccMallocTransaction(book);
+  xaccTransBeginEdit(trans_line->trans);
+  xaccTransSetCurrency(trans_line->trans, currency);
 
+  /* Go through each of the properties and edit the transaction accordingly. */
   list->properties = properties_begin;
   while(list->properties != NULL)
   {
@@ -861,39 +876,60 @@
     switch(prop->type)
     {
     case GNC_CSV_DATE:
-      xaccTransSetDatePostedSecs(trans, *((time_t*)(prop->value)));
+      xaccTransSetDatePostedSecs(trans_line->trans, *((time_t*)(prop->value)));
       break;
 
     case GNC_CSV_DESCRIPTION:
-      xaccTransSetDescription(trans, (char*)(prop->value));
+      xaccTransSetDescription(trans_line->trans, (char*)(prop->value));
       break;
 
-    case GNC_CSV_DEPOSIT:
-    case GNC_CSV_WITHDRAWAL:
-    case GNC_CSV_BALANCE:
-      if(noAmountYet && prop->value != NULL)
+    case GNC_CSV_NUM:
+      xaccTransSetNum(trans_line->trans, (char*)(prop->value));
+      break;
+
+    case GNC_CSV_DEPOSIT: /* Add deposits to the existing amount. */
+      if(prop->value != NULL)
       {
-        /* Withdrawals are just negative deposits. */
-        if(prop->type == GNC_CSV_WITHDRAWAL)
-          amount = gnc_numeric_neg(*((gnc_numeric*)(prop->value)));
-        else
-          amount = *((gnc_numeric*)(prop->value));
+        amount = gnc_numeric_add(*((gnc_numeric*)(prop->value)),
+                                 amount,
+                                 xaccAccountGetCommoditySCU(list->account),
+                                 GNC_RND_ROUND);
+        amount_set = TRUE;
+        /* We will use the "Deposit" and "Withdrawal" columns in preference to "Balance". */
+        trans_line->balance_set = FALSE;
+      }
+      break;
 
-        trans_add_split(trans, list->account, book, amount);
-        
-        noAmountYet = FALSE;
+    case GNC_CSV_WITHDRAWAL: /* Withdrawals are just negative deposits. */
+      if(prop->value != NULL)
+      {
+        amount = gnc_numeric_add(gnc_numeric_neg(*((gnc_numeric*)(prop->value))),
+                                 amount,
+                                 xaccAccountGetCommoditySCU(list->account),
+                                 GNC_RND_ROUND);
+        amount_set = TRUE;
+        /* We will use the "Deposit" and "Withdrawal" columns in preference to "Balance". */
+        trans_line->balance_set = FALSE;
       }
+      break;
+
+    case GNC_CSV_BALANCE: /* The balance gets stored in a separate field in trans_line. */
+      /* We will use the "Deposit" and "Withdrawal" columns in preference to "Balance". */
+      if(!amount_set && prop->value != NULL)
+      {
+        /* This gets put into the actual transaction at the end of gnc_csv_parse_to_trans. */
+        trans_line->balance = *((gnc_numeric*)(prop->value));
+        trans_line->balance_set = TRUE;
+      }
+      break;
     }
     list->properties = g_list_next(list->properties);
   }
 
-  if(noAmountYet)
-  {
-    amount = double_to_gnc_numeric(0.0, xaccAccountGetCommoditySCU(list->account), GNC_RND_ROUND);
-    trans_add_split(trans, list->account, book, amount);
-  }
+  /* Add a split with the cumulative amount value. */
+  trans_add_split(trans_line->trans, list->account, book, amount);
 
-  return trans;
+  return trans_line;
 }
 
 /** Creates a list of transactions from parsed data. Transactions that
@@ -906,12 +942,16 @@
  * @param redo_errors TRUE to convert only error data, FALSE for all data
  * @return 0 on success, 1 on failure
  */
-int gnc_parse_to_trans(GncCsvParseData* parse_data, Account* account,
-                       gboolean redo_errors)
+int gnc_csv_parse_to_trans(GncCsvParseData* parse_data, Account* account,
+                           gboolean redo_errors)
 {
+  gboolean hasBalanceColumn;
   int i, j, max_cols = 0;
   GArray* column_types = parse_data->column_types;
   GList *error_lines = NULL, *begin_error_lines = NULL;
+
+  /* last_transaction points to the last element in
+   * parse_data->transactions, or NULL if it's empty. */
   GList* last_transaction = NULL;
 
   /* Free parse_data->error_lines and parse_data->transactions if they
@@ -920,15 +960,34 @@
   {
     begin_error_lines = error_lines = parse_data->error_lines;
   }
-  else if(parse_data->error_lines != NULL)
+  else
   {
-    g_list_free(parse_data->error_lines);
+    if(parse_data->error_lines != NULL)
+    {
+      g_list_free(parse_data->error_lines);
+    }
+    if(parse_data->transactions != NULL)
+    {
+      g_list_free(parse_data->transactions);
+    }
   }
   parse_data->error_lines = NULL;
 
   if(redo_errors) /* If we're looking only at error data ... */
   {
-    last_transaction = parse_data->transactions;
+    if(parse_data->transactions == NULL)
+    {
+      last_transaction = NULL;
+    }
+    else
+    {
+      /* Move last_transaction to the end. */
+      last_transaction = parse_data->transactions;
+      while(g_list_next(last_transaction) != NULL)
+      {
+        last_transaction = g_list_next(last_transaction);
+      }
+    }
     /* ... we use only the lines in error_lines. */
     if(error_lines == NULL)
       i = parse_data->orig_lines->len; /* Don't go into the for loop. */
@@ -940,6 +999,7 @@
     /* The following while-loop effectively behaves like the following for-loop:
      * for(i = 0; i < parse_data->orig_lines->len; i++). */
     i = 0;
+    last_transaction = NULL;
   }
   while(i < parse_data->orig_lines->len)
   {
@@ -948,11 +1008,8 @@
     gboolean errors = FALSE;
     gchar* error_message = NULL;
     TransPropertyList* list = trans_property_list_new(account, parse_data->date_format);
-    Transaction* trans = NULL;
+    GncCsvTransLine* trans_line;
 
-    /* TODO There should eventually be a specification of what errors
-     * actually occurred, not just that one happened. */
-
     for(j = 0; j < line->len; j++)
     {
       /* We do nothing in "None" columns. */
@@ -980,8 +1037,8 @@
     /* If we had success, add the transaction to parse_data->transaction. */
     if(!errors)
     {
-      trans = trans_property_list_to_trans(list, &error_message);
-      errors = trans == NULL;
+      trans_line = trans_property_list_to_trans(list, &error_message);
+      errors = trans_line == NULL;
     }
 
     trans_property_list_free(list);
@@ -1006,26 +1063,41 @@
     else
     {
       /* If all went well, add this transaction to the list. */
-      GncCsvTransLine* trans_line = g_new(GncCsvTransLine, 1);
-
-      trans_line->trans = trans;
       trans_line->line_no = i;
 
-      if(redo_errors)
+      /* We keep the transactions sorted by date. We start at the end
+       * of the list and go backward, simply because the file itself
+       * is probably also sorted by date (but we need to handle the
+       * exception anyway). */
+
+      /* If we can just put it at the end, do so and increment last_transaction. */
+      if(last_transaction == NULL ||
+         xaccTransGetDate(((GncCsvTransLine*)(last_transaction->data))->trans) <= xaccTransGetDate(trans_line->trans))
       {
-        /* If we are correcting errors, find the right place to insert
-         * the transaction. */
-        while(last_transaction != NULL &&
-              ((GncCsvTransLine*)(last_transaction->data))->line_no < i)
-        {
+        parse_data->transactions = g_list_append(parse_data->transactions, trans_line);
+        /* If this is the first transaction, we need to get last_transaction on track. */
+        if(last_transaction == NULL)
+          last_transaction = parse_data->transactions;
+        else /* Otherwise, we can just continue. */
           last_transaction = g_list_next(last_transaction);
-        }
-        parse_data->transactions =
-          g_list_insert_before(parse_data->transactions, last_transaction, trans_line);
       }
+      /* Otherwise, search backward for the correct spot. */
       else
       {
-        parse_data->transactions = g_list_append(parse_data->transactions, trans_line);
+        GList* insertion_spot = last_transaction;
+        while(insertion_spot != NULL &&
+              xaccTransGetDate(((GncCsvTransLine*)(insertion_spot->data))->trans) > xaccTransGetDate(trans_line->trans))
+        {
+          insertion_spot = g_list_previous(insertion_spot);
+        }
+        /* Move insertion_spot one location forward since we have to
+         * use the g_list_insert_before function. */
+        if(insertion_spot == NULL) /* We need to handle the case of inserting at the beginning of the list. */
+          insertion_spot = parse_data->transactions;
+        else
+          insertion_spot = g_list_next(insertion_spot);
+        
+        parse_data->transactions = g_list_insert_before(parse_data->transactions, insertion_spot, trans_line);
       }
     }
 
@@ -1044,8 +1116,69 @@
       i++;
     }
   }
-  if(redo_errors)
+
+  /* If we have a balance column, set the appropriate amounts on the transactions. */
+  hasBalanceColumn = FALSE;
+  for(i = 0; i < parse_data->column_types->len; i++)
   {
+    if(parse_data->column_types->data[i] == GNC_CSV_BALANCE)
+    {
+      hasBalanceColumn = TRUE;
+      break;
+    }
+  }
+
+  if(hasBalanceColumn)
+  {
+    GList* transactions = parse_data->transactions;
+
+    /* balance_offset is how much the balance currently in the account
+     * differs from what it will be after the transactions are
+     * imported. This will be sum of all the previous transactions for
+     * any given transaction. */
+    gnc_numeric balance_offset = double_to_gnc_numeric(0.0,
+                                                       xaccAccountGetCommoditySCU(account),
+                                                       GNC_RND_ROUND);
+    while(transactions != NULL)
+    {
+      GncCsvTransLine* trans_line = (GncCsvTransLine*)transactions->data;
+      if(trans_line->balance_set)
+      {
+        time_t date = xaccTransGetDate(trans_line->trans);
+        /* Find what the balance should be by adding the offset to the actual balance. */
+        gnc_numeric existing_balance = gnc_numeric_add(balance_offset,
+                                                       xaccAccountGetBalanceAsOfDate(account, date),
+                                                       xaccAccountGetCommoditySCU(account),
+                                                       GNC_RND_ROUND);
+
+        /* The amount of the transaction is the difference between the new and existing balance. */
+        gnc_numeric amount = gnc_numeric_sub(trans_line->balance,
+                                             existing_balance,
+                                             xaccAccountGetCommoditySCU(account),
+                                             GNC_RND_ROUND);
+
+        SplitList* splits = xaccTransGetSplitList(trans_line->trans);
+        while(splits)
+        {
+          SplitList* next_splits = g_list_next(splits);
+          xaccSplitDestroy((Split*)splits->data);
+          splits = next_splits;
+        }
+
+        trans_add_split(trans_line->trans, account, gnc_account_get_book(account), amount);
+        
+        /* This new transaction needs to be added to the balance offset. */
+        balance_offset = gnc_numeric_add(balance_offset,
+                                         amount,
+                                         xaccAccountGetCommoditySCU(account),
+                                         GNC_RND_ROUND);
+      }
+      transactions = g_list_next(transactions);
+    }
+  }
+
+  if(redo_errors) /* Now that we're at the end, we do the freeing. */
+  {
     g_list_free(begin_error_lines);
   }
 

Modified: gnucash/branches/csv-import/src/import-export/csv/gnc-csv-model.h
===================================================================
--- gnucash/branches/csv-import/src/import-export/csv/gnc-csv-model.h	2007-08-29 22:16:59 UTC (rev 16479)
+++ gnucash/branches/csv-import/src/import-export/csv/gnc-csv-model.h	2007-08-30 04:55:36 UTC (rev 16480)
@@ -44,6 +44,7 @@
                        GNC_CSV_BALANCE,
                        GNC_CSV_DEPOSIT,
                        GNC_CSV_WITHDRAWAL,
+                       GNC_CSV_NUM,
                        GNC_CSV_NUM_COL_TYPES};
 
 /** Enumeration for error types. These are the different types of
@@ -61,6 +62,9 @@
   char* end;
 } GncCsvStr;
 
+/* TODO We now sort transactions by date, not line number, so we
+ * should probably get rid of this struct and uses of it. */
+
 /** Struct pairing a transaction with a line number. This struct is
  * used to keep the transactions in order. When rows are separated
  * into "valid" and "error" lists (in case some of the rows have cells
@@ -72,6 +76,8 @@
 {
   int line_no;
   Transaction* trans;
+  gnc_numeric balance; /**< The (supposed) balance after this transaction takes place */
+  gboolean balance_set; /**< TRUE if balance has been set from user data, FALSE otherwise */
 } GncCsvTransLine;
 
 extern const int num_date_formats;
@@ -111,6 +117,6 @@
 
 int gnc_csv_parse(GncCsvParseData* parse_data, gboolean guessColTypes, GError** error);
 
-int gnc_parse_to_trans(GncCsvParseData* parse_data, Account* account, gboolean redo_errors);
+int gnc_csv_parse_to_trans(GncCsvParseData* parse_data, Account* account, gboolean redo_errors);
 
 #endif



More information about the gnucash-changes mailing list