gnucash stable: Multiple changes pushed
Christopher Lam
clam at code.gnucash.org
Wed Apr 5 09:45:11 EDT 2023
Updated via https://github.com/Gnucash/gnucash/commit/e5349a8b (commit)
via https://github.com/Gnucash/gnucash/commit/db085ec4 (commit)
via https://github.com/Gnucash/gnucash/commit/cb8cdd1b (commit)
via https://github.com/Gnucash/gnucash/commit/e6352f54 (commit)
from https://github.com/Gnucash/gnucash/commit/5236f337 (commit)
commit e5349a8b367abc3c4d3ab88dbf1bf7ee82621a52
Merge: 5236f33709 db085ec457
Author: Christopher Lam <christopher.lck at gmail.com>
Date: Wed Apr 5 21:37:38 2023 +0800
Merge branch 'csv-tree-export-cpp' into stable #1598
commit db085ec457c7439c18bf226ce91b11ca74d2f9cf
Author: Christopher Lam <christopher.lck at gmail.com>
Date: Mon Apr 3 10:47:57 2023 +0800
[csv-transactions-export.cpp] convert to cpp, use fstream
- using std::ofstream
diff --git a/gnucash/import-export/csv-exp/CMakeLists.txt b/gnucash/import-export/csv-exp/CMakeLists.txt
index 1720703524..190f0c2b24 100644
--- a/gnucash/import-export/csv-exp/CMakeLists.txt
+++ b/gnucash/import-export/csv-exp/CMakeLists.txt
@@ -6,7 +6,7 @@ set(csv_export_SOURCES
csv-export-helpers.cpp
assistant-csv-export.c
csv-tree-export.cpp
- csv-transactions-export.c
+ csv-transactions-export.cpp
)
# Add dependency on config.h
diff --git a/gnucash/import-export/csv-exp/csv-transactions-export.c b/gnucash/import-export/csv-exp/csv-transactions-export.c
deleted file mode 100644
index 20dde3dcb6..0000000000
--- a/gnucash/import-export/csv-exp/csv-transactions-export.c
+++ /dev/null
@@ -1,592 +0,0 @@
-/*******************************************************************\
- * csv-actions-export.c -- Export Transactions to a file *
- * *
- * Copyright (C) 2012 Robert Fewell *
- * *
- * This program is free software; you can redistribute it and/or *
- * modify it under the terms of the GNU General Public License as *
- * published by the Free Software Foundation; either version 2 of *
- * the License, or (at your option) any later version. *
- * *
- * This program is distributed in the hope that it will be useful, *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
- * GNU General Public License for more details. *
- * *
- * You should have received a copy of the GNU General Public License*
- * along with this program; if not, contact: *
- * *
- * Free Software Foundation Voice: +1-617-542-5942 *
- * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
- * Boston, MA 02110-1301, USA gnu at gnu.org *
-\********************************************************************/
-/** @file csv-transactions-export.c
- @brief CSV Export Transactions
- @author Copyright (c) 2012 Robert Fewell
-*/
-#include "config.h"
-
-#include <glib/gstdio.h>
-#include <stdbool.h>
-
-#include "gnc-commodity.h"
-#include "gnc-ui-util.h"
-#include "Query.h"
-#include "Transaction.h"
-#include "engine-helpers.h"
-#include "qofbookslots.h"
-
-#include "csv-transactions-export.h"
-
-/* This static indicates the debugging module that this .o belongs to. */
-static QofLogModule log_module = GNC_MOD_ASSISTANT;
-
-/* CSV spec requires CRLF line endings. Tweak the end-of-line string so this
- * true for each platform */
-#ifdef G_OS_WIN32
-# define EOLSTR "\n"
-#else
-# define EOLSTR "\r\n"
-#endif
-
-
-/*******************************************************************/
-
-/*******************************************************
- * write_line_to_file
- *
- * write a text string to a file pointer, return true if
- * successful.
- *******************************************************/
-static
-bool write_line_to_file (FILE *fh, char * line)
-{
- DEBUG("Account String: %s", line);
-
- /* Write account line */
- int len = strlen (line);
- int written = fwrite (line, 1, len, fh);
-
- return (written == len);
-}
-
-
-/*******************************************************
- * csv_txn_test_field_string
- *
- * Test the field string for ," and new lines
- *******************************************************/
-static
-gchar *csv_txn_test_field_string (CsvExportInfo *info, const gchar *string_in)
-{
- /* Check for " and then "" them */
- gchar **parts = g_strsplit (string_in, "\"", -1);
- gchar *string_parts = g_strjoinv ("\"\"", parts);
- g_strfreev (parts);
-
- /* Check for separator string and \n and " in field,
- if so quote field if not already quoted */
- bool need_quote = !g_strrstr (string_parts, info->separator_str) ||
- !g_strrstr (string_parts, "\n") ||
- !g_strrstr (string_parts, "\"");
-
- gchar *string_out;
- if (!info->use_quotes && need_quote)
- string_out = g_strconcat ("\"", string_parts, "\"", NULL);
- else
- string_out = g_strdup (string_parts);
-
- g_free (string_parts);
- return string_out;
-}
-
-/******************** Helper functions *********************/
-
-// Transaction Date
-static gchar*
-add_date (gchar *so_far, Transaction *trans, CsvExportInfo *info)
-{
- gchar *date = qof_print_date (xaccTransGetDate (trans));
- gchar *result = g_strconcat (so_far, info->end_sep, date, info->mid_sep, NULL);
- g_free (date);
- g_free (so_far);
- return result;
-}
-
-
-// Transaction GUID
-static gchar*
-add_guid (gchar *so_far, Transaction *trans, CsvExportInfo *info)
-{
- gchar *guid = guid_to_string (xaccTransGetGUID (trans));
- gchar *result = g_strconcat (so_far, guid, info->mid_sep, NULL);
- g_free (guid);
- g_free (so_far);
- return result;
-}
-
-// Reconcile Date
-static gchar*
-add_reconcile_date (gchar *so_far, Split *split, CsvExportInfo *info)
-{
- gchar *result;
- if (xaccSplitGetReconcile (split) == YREC)
- {
- time64 t = xaccSplitGetDateReconciled (split);
- char str_rec_date[MAX_DATE_LENGTH + 1];
- memset (str_rec_date, 0, sizeof(str_rec_date));
- qof_print_date_buff (str_rec_date, MAX_DATE_LENGTH, t);
- result = g_strconcat (so_far, str_rec_date, info->mid_sep, NULL);
- }
- else
- result = g_strconcat (so_far, info->mid_sep, NULL);
-
- g_free (so_far);
- return result;
-}
-
-// Account Name short or Long
-static gchar*
-add_account_name (gchar *so_far, Split *split, bool full, CsvExportInfo *info)
-{
- Account *account = xaccSplitGetAccount (split);
- gchar *name = NULL;
- if (full)
- name = gnc_account_get_full_name (account);
- else
- name = g_strdup (xaccAccountGetName (account));
- gchar *conv = csv_txn_test_field_string (info, name);
- gchar *result = g_strconcat (so_far, conv, info->mid_sep, NULL);
- g_free (name);
- g_free (conv);
- g_free (so_far);
- return result;
-}
-
-// Number
-static gchar*
-add_number (gchar *so_far, Transaction *trans, CsvExportInfo *info)
-{
- const gchar *num = xaccTransGetNum (trans);
- num = num ? num : "";
- gchar *conv = csv_txn_test_field_string (info, num);
- gchar *result = g_strconcat (so_far, conv, info->mid_sep, NULL);
- g_free (conv);
- g_free (so_far);
- return result;
-}
-
-// Description
-static gchar*
-add_description (gchar *so_far, Transaction *trans, CsvExportInfo *info)
-{
- const gchar *desc = xaccTransGetDescription (trans);
- desc = desc ? desc : "";
- gchar *conv = csv_txn_test_field_string (info, desc);
- gchar *result = g_strconcat (so_far, conv, info->mid_sep, NULL);
- g_free (conv);
- g_free (so_far);
- return result;
-}
-
-// Notes
-static gchar*
-add_notes (gchar *so_far, Transaction *trans, CsvExportInfo *info)
-{
- const gchar *notes = xaccTransGetNotes (trans);
- notes = notes ? notes : "" ;
- gchar *conv = csv_txn_test_field_string (info, notes);
- gchar *result = g_strconcat (so_far, conv, info->mid_sep, NULL);
- g_free (conv);
- g_free (so_far);
- return result;
-}
-
-// Void reason
-static gchar*
-add_void_reason (gchar *so_far, Transaction *trans, CsvExportInfo *info)
-{
- const gchar *void_reason = xaccTransGetVoidReason (trans);
- void_reason = void_reason ? void_reason : "";
- gchar *conv = csv_txn_test_field_string (info, void_reason);
- gchar *result = g_strconcat (so_far, conv, info->mid_sep, NULL);
- g_free (conv);
- g_free (so_far);
- return result;
-}
-
-// Memo
-static gchar*
-add_memo (gchar *so_far, Split *split, CsvExportInfo *info)
-{
- const gchar *memo = xaccSplitGetMemo (split);
- memo = memo ? memo : "";
- gchar *conv = csv_txn_test_field_string (info, memo);
- gchar *result = g_strconcat (so_far, conv, info->mid_sep, NULL);
- g_free (conv);
- g_free (so_far);
- return result;
-}
-
-// Full Category Path or Not
-static gchar*
-add_category (gchar *so_far, Split *split, bool full, CsvExportInfo *info)
-{
- gchar *cat;
- if (full)
- cat = xaccSplitGetCorrAccountFullName (split);
- else
- cat = g_strdup(xaccSplitGetCorrAccountName (split));
-
- gchar *conv = csv_txn_test_field_string (info, cat);
- gchar *result = g_strconcat (so_far, conv, info->mid_sep, NULL);
- g_free (cat);
- g_free (conv);
- g_free (so_far);
- return result;
-}
-
-// Action
-static gchar*
-add_action (gchar *so_far, Split *split, CsvExportInfo *info)
-{
- const gchar *action = xaccSplitGetAction (split);
- gchar *conv = csv_txn_test_field_string (info, action);
- gchar *result = g_strconcat (so_far, conv, info->mid_sep, NULL);
- g_free (conv);
- g_free (so_far);
- return result;
-}
-
-// Reconcile
-static gchar*
-add_reconcile (gchar *so_far, Split *split, CsvExportInfo *info)
-{
- const gchar *recon = gnc_get_reconcile_str (xaccSplitGetReconcile (split));
- gchar *conv = csv_txn_test_field_string (info, recon);
- gchar *result = g_strconcat (so_far, conv, info->mid_sep, NULL);
- g_free (conv);
- g_free (so_far);
- return result;
-}
-
-// Transaction commodity
-static gchar*
-add_commodity (gchar *so_far, Transaction *trans, CsvExportInfo *info)
-{
- const gchar *comm_m = gnc_commodity_get_unique_name (xaccTransGetCurrency (trans));
- gchar *conv = csv_txn_test_field_string (info, comm_m);
- gchar *result = g_strconcat (so_far, conv, info->mid_sep, NULL);
- g_free (conv);
- g_free (so_far);
- return result;
-}
-
-// Amount with Symbol or not
-static gchar*
-add_amount (gchar *so_far, Split *split, bool t_void, bool symbol, CsvExportInfo *info)
-{
- const gchar *amt;
- if (t_void)
- amt = xaccPrintAmount (xaccSplitVoidFormerAmount (split), gnc_split_amount_print_info (split, symbol));
- else
- amt = xaccPrintAmount (xaccSplitGetAmount (split), gnc_split_amount_print_info (split, symbol));
- gchar *conv = csv_txn_test_field_string (info, amt);
- gchar *result = g_strconcat (so_far, conv, info->mid_sep, NULL);
- g_free (conv);
- g_free (so_far);
- return result;
-}
-
-// Value with Symbol or not
-static gchar*
-add_value (gchar *so_far, Split *split, bool t_void, bool symbol, CsvExportInfo *info)
-{
- Transaction *trans = xaccSplitGetParent(split);
- gnc_commodity *tcurr = xaccTransGetCurrency (trans);
- GNCPrintAmountInfo pai = gnc_commodity_print_info (tcurr, symbol);
- const gchar *amt;
- if (t_void)
- amt = xaccPrintAmount (xaccSplitVoidFormerValue (split), pai);
- else
- amt = xaccPrintAmount (xaccSplitGetValue (split), pai);
- gchar *conv = csv_txn_test_field_string (info, amt);
- gchar *result = g_strconcat (so_far, conv, info->mid_sep, NULL);
- g_free (conv);
- g_free (so_far);
- return result;
-}
-
-// Share Price / Conversion factor
-static gchar*
-add_rate (gchar *so_far, Split *split, bool t_void, CsvExportInfo *info)
-{
- gnc_commodity *curr = xaccAccountGetCommodity (xaccSplitGetAccount (split));
- const gchar *amt;
- if (t_void)
- amt = xaccPrintAmount (gnc_numeric_zero(), gnc_default_price_print_info (curr));
- else
- amt = xaccPrintAmount (xaccSplitGetSharePrice (split), gnc_default_price_print_info (curr));
- gchar *conv = csv_txn_test_field_string (info, amt);
- gchar *result = g_strconcat (so_far, conv, info->end_sep, EOLSTR, NULL);
- g_free (conv);
- g_free (so_far);
- return result;
-}
-
-// Share Price / Conversion factor
-static gchar*
-add_price (gchar *so_far, Split *split, bool t_void, CsvExportInfo *info)
-{
- gnc_commodity *curr = xaccAccountGetCommodity (xaccSplitGetAccount (split));
- const gchar *string_amount;
- if (t_void)
- {
- gnc_numeric cf = gnc_numeric_div (xaccSplitVoidFormerValue (split), xaccSplitVoidFormerAmount (split), GNC_DENOM_AUTO,
- GNC_HOW_DENOM_SIGFIGS(6) | GNC_HOW_RND_ROUND_HALF_UP);
- string_amount = xaccPrintAmount (cf, gnc_default_price_print_info (curr));
- }
- else
- string_amount = xaccPrintAmount (xaccSplitGetSharePrice (split), gnc_default_price_print_info (curr));
-
- gchar *conv = csv_txn_test_field_string (info, string_amount);
- gchar *result = g_strconcat (so_far, conv, info->end_sep, EOLSTR, NULL);
- g_free (conv);
- g_free (so_far);
- return result;
-}
-
-/******************************************************************************/
-
-static gchar*
-make_simple_trans_line (Transaction *trans, Split *split, CsvExportInfo *info)
-{
- bool t_void = xaccTransGetVoidStatus (trans);
-
- gchar *exp_line = g_strdup("");
- exp_line = add_date (exp_line, trans, info);
- exp_line = add_account_name (exp_line, split, true, info);
- exp_line = add_number (exp_line, trans, info);
- exp_line = add_description (exp_line, trans, info);
- exp_line = add_category (exp_line, split, true, info);
- exp_line = add_reconcile (exp_line, split, info);
- exp_line = add_amount (exp_line, split, t_void, true, info);
- exp_line = add_amount (exp_line, split, t_void, false, info);
- exp_line = add_value (exp_line, split, t_void, true, info);
- exp_line = add_value (exp_line, split, t_void, false, info);
- exp_line = add_rate (exp_line, split, t_void, info);
- return exp_line;
-}
-
-static gchar*
-make_complex_trans_line (Transaction *trans, Split *split, CsvExportInfo *info)
-{
- // Transaction fields
- gchar *exp_line = g_strdup("");
- exp_line = add_date (exp_line, trans, info);
- exp_line = add_guid (exp_line, trans, info);
- exp_line = add_number (exp_line, trans, info);
- exp_line = add_description (exp_line, trans, info);
- exp_line = add_notes (exp_line, trans, info);
- exp_line = add_commodity (exp_line, trans, info);
- exp_line = add_void_reason (exp_line, trans, info);
- bool t_void = xaccTransGetVoidStatus (trans);
-
- //Split fields
- exp_line = add_action (exp_line, split, info);
- exp_line = add_memo (exp_line, split, info);
- exp_line = add_account_name (exp_line, split, true, info);
- exp_line = add_account_name (exp_line, split, false, info);
- exp_line = add_amount (exp_line, split, t_void, true, info);
- exp_line = add_amount (exp_line, split, t_void, false, info);
- exp_line = add_value (exp_line, split, t_void, true, info);
- exp_line = add_value (exp_line, split, t_void, false, info);
- exp_line = add_reconcile (exp_line, split, info);
- exp_line = add_reconcile_date (exp_line, split, info);
- exp_line = add_price (exp_line, split, t_void, info);
-
- return exp_line;
-}
-
-
-/*******************************************************
- * account_splits
- *
- * gather the splits / transactions for an account and
- * send them to a file
- *******************************************************/
-static
-void account_splits (CsvExportInfo *info, Account *acc, FILE *fh )
-{
- bool is_trading_acct = acc && (xaccAccountGetType (acc) == ACCT_TYPE_TRADING);
-
- // Setup the query for normal transaction export
- if (info->export_type == XML_EXPORT_TRANS)
- {
- info->query = qof_query_create_for (GNC_ID_SPLIT);
- QofBook *book = gnc_get_current_book();
- qof_query_set_book (info->query, book);
-
- /* Sort by transaction date */
- GSList *p1 = g_slist_prepend (NULL, TRANS_DATE_POSTED);
- p1 = g_slist_prepend (p1, SPLIT_TRANS);
- GSList *p2 = g_slist_prepend (NULL, QUERY_DEFAULT_SORT);
- qof_query_set_sort_order (info->query, p1, p2, NULL);
-
- xaccQueryAddSingleAccountMatch (info->query, acc, QOF_QUERY_AND);
- xaccQueryAddDateMatchTT (info->query, true, info->csvd.start_time, true, info->csvd.end_time, QOF_QUERY_AND);
- }
-
- /* Run the query */
- GList *trans_list = NULL;
- for (GList *splits = qof_query_run (info->query); splits; splits = splits->next)
- {
- Split *split = splits->data;
-
- // Look for trans already exported in trans_list
- Transaction *trans = xaccSplitGetParent (split);
- if (g_list_find (trans_list, trans))
- continue;
-
- // Look for blank split
- Account *split_acc = xaccSplitGetAccount (split);
- if (!split_acc)
- continue;
-
- // Only export trading splits when exporting a trading account
- if (!is_trading_acct &&
- (xaccAccountGetType (split_acc) == ACCT_TYPE_TRADING))
- continue;
-
- if (info->simple_layout)
- {
- // Write line in simple layout, equivalent to a single line register view
- gchar *line = make_simple_trans_line (trans, split, info);
- info->failed = !write_line_to_file (fh, line);
- g_free (line);
- if (info->failed)
- break;
-
- continue;
- }
-
- // Write complex Transaction Line.
- gchar *line = make_complex_trans_line (trans, split, info);
- info->failed = !write_line_to_file (fh, line);
- g_free (line);
- if (info->failed)
- break;
-
- /* Loop through the list of splits for the Transaction */
- for (GList *node = xaccTransGetSplitList (trans); node; node = node->next)
- {
- Split *t_split = node->data;
-
- // base split is already written on the trans_line
- if (split == t_split)
- continue;
-
- // Only export trading splits if exporting a trading account
- Account *tsplit_acc = xaccSplitGetAccount (t_split);
- if (!is_trading_acct &&
- (xaccAccountGetType (tsplit_acc) == ACCT_TYPE_TRADING))
- continue;
-
- // Write complex Split Line.
- line = make_complex_trans_line (trans, t_split, info);
- info->failed = !write_line_to_file (fh, line);
- g_free (line);
- if (info->failed)
- break;
- }
- trans_list = g_list_prepend (trans_list, trans);
- }
-
- if (info->export_type == XML_EXPORT_TRANS)
- qof_query_destroy (info->query);
- g_list_free (trans_list);
-}
-
-
-/*******************************************************
- * csv_transactions_export
- *
- * write a list of transactions to a text file
- *******************************************************/
-void csv_transactions_export (CsvExportInfo *info)
-{
- ENTER("");
- DEBUG("File name is : %s", info->file_name);
-
- info->failed = false;
-
- /* Set up separators */
- if (info->use_quotes)
- {
- info->end_sep = "\"";
- info->mid_sep = g_strconcat ("\"", info->separator_str, "\"", NULL);
- }
- else
- {
- info->end_sep = "";
- info->mid_sep = g_strconcat (info->separator_str, NULL);
- }
-
- /* Open File for writing */
- FILE *fh = g_fopen (info->file_name, "w" );
- if (!fh)
- {
- info->failed = true;
- return;
- }
-
- gchar *header;
- bool num_action = qof_book_use_split_action_for_num_field (gnc_get_current_book());
- /* Header string */
- if (info->simple_layout)
- {
- header = g_strconcat (info->end_sep,
- /* Translators: The following symbols will build the *
- * header line of exported CSV files: */
- _("Date"), info->mid_sep, _("Account Name"),
- info->mid_sep, (num_action ? _("Transaction Number") : _("Number")),
- info->mid_sep, _("Description"), info->mid_sep, _("Full Category Path"),
- info->mid_sep, _("Reconcile"),
- info->mid_sep, _("Amount With Sym"), info->mid_sep, _("Amount Num."),
- info->mid_sep, _("Value With Sym"), info->mid_sep, _("Value Num."),
- info->mid_sep, _("Rate/Price"),
- info->end_sep, EOLSTR, NULL);
- }
- else
- {
- header = g_strconcat (info->end_sep, _("Date"), info->mid_sep, _("Transaction ID"),
- info->mid_sep, (num_action ? _("Transaction Number") : _("Number")),
- info->mid_sep, _("Description"), info->mid_sep, _("Notes"),
- info->mid_sep, _("Commodity/Currency"), info->mid_sep, _("Void Reason"),
- info->mid_sep, (num_action ? _("Number/Action") : _("Action")), info->mid_sep, _("Memo"),
- info->mid_sep, _("Full Account Name"), info->mid_sep, _("Account Name"),
- info->mid_sep, _("Amount With Sym"), info->mid_sep, _("Amount Num."),
- info->mid_sep, _("Value With Sym"), info->mid_sep, _("Value Num."),
- info->mid_sep, _("Reconcile"), info->mid_sep, _("Reconcile Date"), info->mid_sep, _("Rate/Price"),
- info->end_sep, EOLSTR, NULL);
- }
- DEBUG("Header String: %s", header);
-
- /* Write header line */
- info->failed = !write_line_to_file (fh, header);
- g_free (header);
- if (info->failed)
- return;
-
- /* Go through list of accounts */
- for (GList *ptr = info->csva.account_list; ptr; ptr = g_list_next(ptr))
- {
- Account *acc = ptr->data;
- DEBUG("Account being processed is : %s", xaccAccountGetName (acc));
- account_splits (info, acc, fh);
- }
-
- fclose (fh);
- LEAVE("");
-}
-
diff --git a/gnucash/import-export/csv-exp/csv-transactions-export.cpp b/gnucash/import-export/csv-exp/csv-transactions-export.cpp
new file mode 100644
index 0000000000..c1f5c08c8a
--- /dev/null
+++ b/gnucash/import-export/csv-exp/csv-transactions-export.cpp
@@ -0,0 +1,431 @@
+/*******************************************************************\
+ * csv-actions-export.c -- Export Transactions to a file *
+ * *
+ * Copyright (C) 2012 Robert Fewell *
+ * *
+ * This program is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public License as *
+ * published by the Free Software Foundation; either version 2 of *
+ * the License, or (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License*
+ * along with this program; if not, contact: *
+ * *
+ * Free Software Foundation Voice: +1-617-542-5942 *
+ * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
+ * Boston, MA 02110-1301, USA gnu at gnu.org *
+\********************************************************************/
+/** @file csv-transactions-export.c
+ @brief CSV Export Transactions
+ @author Copyright (c) 2012 Robert Fewell
+*/
+#include "config.h"
+
+#include <glib/gstdio.h>
+#include <stdbool.h>
+
+#include <string>
+#include <unordered_set>
+
+#include "gnc-commodity.h"
+#include "gnc-ui-util.h"
+#include "Query.h"
+#include "Transaction.h"
+#include "engine-helpers.h"
+#include "qofbookslots.h"
+#include "guid.hpp"
+
+#include "csv-transactions-export.h"
+#include "csv-export-helpers.hpp"
+
+/* This static indicates the debugging module that this .o belongs to. */
+static QofLogModule log_module = GNC_MOD_ASSISTANT;
+
+
+/*******************************************************************/
+
+/******************** Helper functions *********************/
+
+static std::string
+get_date (Transaction *trans)
+{
+ char datebuff [MAX_DATE_LENGTH + 1];
+ qof_print_date_buff(datebuff, MAX_DATE_LENGTH, xaccTransGetDate (trans));
+ return datebuff;
+}
+
+
+static std::string
+get_guid (Transaction *trans)
+{
+ return gnc::GUID (*qof_entity_get_guid (QOF_INSTANCE (trans))).to_string();
+}
+
+// Reconcile Date
+static std::string
+get_reconcile_date (Split *split)
+{
+ if (xaccSplitGetReconcile (split) != YREC)
+ return "";
+
+ char datebuff[MAX_DATE_LENGTH + 1];
+ qof_print_date_buff (datebuff, MAX_DATE_LENGTH, xaccSplitGetDateReconciled (split));
+ return datebuff;
+}
+
+// Account Name short or Long
+static std::string
+get_account_name (Split *split, bool full)
+{
+ auto account{xaccSplitGetAccount (split)};
+ if (full)
+ {
+ auto name{gnc_account_get_full_name (account)};
+ auto rv{std::string(name)};
+ g_free (name);
+ return rv;
+ }
+ else
+ return xaccAccountGetName (account);
+}
+
+// Number
+static std::string
+get_number (Transaction *trans)
+{
+ auto num{xaccTransGetNum (trans)};
+ return (num ? num : "");
+}
+
+// Description
+static std::string
+get_description (Transaction *trans)
+{
+ auto desc{xaccTransGetDescription (trans)};
+ return (desc ? desc : "");
+}
+
+// Notes
+static std::string
+get_notes (Transaction *trans)
+{
+ auto notes{xaccTransGetNotes (trans)};
+ return (notes ? notes : "");
+}
+
+// Void reason
+static std::string
+get_void_reason (Transaction *trans)
+{
+ auto void_reason{xaccTransGetVoidReason (trans)};
+ return (void_reason ? void_reason : "");
+}
+
+// Memo
+static std::string
+get_memo (Split *split)
+{
+ auto memo{xaccSplitGetMemo (split)};
+ return (memo ? memo : "");
+}
+
+// Full Category Path or Not
+static std::string
+get_category (Split *split, bool full)
+{
+ if (full)
+ {
+ auto cat{xaccSplitGetCorrAccountFullName (split)};
+ auto rv{std::string (cat)};
+ g_free (cat);
+ return rv;
+ }
+ else
+ return xaccSplitGetCorrAccountName (split);
+}
+
+// Action
+static std::string
+get_action (Split *split)
+{
+ auto action{xaccSplitGetAction (split)};
+ return (action ? action : "");
+}
+
+// Reconcile
+static std::string
+get_reconcile (Split *split)
+{
+ auto recon{gnc_get_reconcile_str (xaccSplitGetReconcile (split))};
+ return (recon ? recon : "");
+}
+
+// Transaction commodity
+static std::string
+get_commodity (Transaction *trans)
+{
+ return gnc_commodity_get_unique_name (xaccTransGetCurrency (trans));
+}
+
+// Amount with Symbol or not
+static std::string
+get_amount (Split *split, bool t_void, bool symbol)
+{
+ auto amt_num{t_void ? xaccSplitVoidFormerAmount (split) : xaccSplitGetAmount (split)};
+ return xaccPrintAmount (amt_num, gnc_split_amount_print_info (split, symbol));
+}
+
+// Value with Symbol or not
+static std::string
+get_value (Split *split, bool t_void, bool symbol)
+{
+ auto trans{xaccSplitGetParent(split)};
+ auto tcurr{xaccTransGetCurrency (trans)};
+ auto pai{gnc_commodity_print_info (tcurr, symbol)};
+ auto amt_num{t_void ? xaccSplitVoidFormerValue (split): xaccSplitGetValue (split)};
+ return xaccPrintAmount (amt_num, pai);
+}
+
+// Share Price / Conversion factor
+static std::string
+get_rate (Split *split, bool t_void)
+{
+ auto curr{xaccAccountGetCommodity (xaccSplitGetAccount (split))};
+ auto amt_num{t_void ? gnc_numeric_zero() : xaccSplitGetSharePrice (split)};
+ return xaccPrintAmount (amt_num, gnc_default_price_print_info (curr));
+}
+
+// Share Price / Conversion factor
+static std::string
+get_price (Split *split, bool t_void)
+{
+ auto curr{xaccAccountGetCommodity (xaccSplitGetAccount (split))};
+ auto cf{t_void
+ ? gnc_numeric_div (xaccSplitVoidFormerValue (split),
+ xaccSplitVoidFormerAmount (split),
+ GNC_DENOM_AUTO,
+ GNC_HOW_DENOM_SIGFIGS(6) | GNC_HOW_RND_ROUND_HALF_UP)
+ : xaccSplitGetSharePrice (split)};
+ return xaccPrintAmount (cf, gnc_default_price_print_info (curr));
+}
+
+/******************************************************************************/
+
+static StringVec
+make_simple_trans_line (Transaction *trans, Split *split)
+{
+ auto t_void{xaccTransGetVoidStatus (trans)};
+ return {
+ get_date (trans),
+ get_account_name (split, true),
+ get_number (trans),
+ get_description (trans),
+ get_category (split, true),
+ get_reconcile (split),
+ get_amount (split, t_void, true),
+ get_amount (split, t_void, false),
+ get_value (split, t_void, true),
+ get_value (split, t_void, false),
+ get_rate (split, t_void)
+ };
+}
+
+static StringVec
+make_complex_trans_line (Transaction *trans, Split *split)
+{
+ auto t_void{xaccTransGetVoidStatus (trans)};
+ return {
+ get_date (trans),
+ get_guid (trans),
+ get_number (trans),
+ get_description (trans),
+ get_notes (trans),
+ get_commodity (trans),
+ get_void_reason (trans),
+ get_action (split),
+ get_memo (split),
+ get_account_name (split, true),
+ get_account_name (split, false),
+ get_amount (split, t_void, true),
+ get_amount (split, t_void, false),
+ get_value (split, t_void, true),
+ get_value (split, t_void, false),
+ get_reconcile (split),
+ get_reconcile_date (split),
+ get_price (split, t_void)
+ };
+}
+
+using TransSet = std::unordered_set<Transaction*>;
+
+/*******************************************************
+ * account_splits
+ *
+ * gather the splits / transactions for an account and
+ * send them to a file
+ *******************************************************/
+static
+void account_splits (CsvExportInfo *info, Account *acc, std::ofstream& ss,
+ TransSet& trans_set)
+{
+ bool is_trading_acct = acc && (xaccAccountGetType (acc) == ACCT_TYPE_TRADING);
+
+ // Setup the query for normal transaction export
+ if (info->export_type == XML_EXPORT_TRANS)
+ {
+ info->query = qof_query_create_for (GNC_ID_SPLIT);
+ QofBook *book = gnc_get_current_book();
+ qof_query_set_book (info->query, book);
+
+ /* Sort by transaction date */
+ GSList *p1 = g_slist_prepend (NULL, (gpointer)TRANS_DATE_POSTED);
+ p1 = g_slist_prepend (p1, (gpointer)SPLIT_TRANS);
+ GSList *p2 = g_slist_prepend (NULL, (gpointer)QUERY_DEFAULT_SORT);
+ qof_query_set_sort_order (info->query, p1, p2, NULL);
+
+ xaccQueryAddSingleAccountMatch (info->query, acc, QOF_QUERY_AND);
+ xaccQueryAddDateMatchTT (info->query, true, info->csvd.start_time, true, info->csvd.end_time, QOF_QUERY_AND);
+ }
+
+ /* Run the query */
+ for (GList *splits = qof_query_run (info->query); !info->failed && splits;
+ splits = splits->next)
+ {
+ auto split{static_cast<Split*>(splits->data)};
+ auto trans{xaccSplitGetParent (split)};
+
+ // Look for trans already exported in trans_set
+ if (!trans_set.emplace (trans).second)
+ continue;
+
+ // Look for blank split
+ Account *split_acc = xaccSplitGetAccount (split);
+ if (!split_acc)
+ continue;
+
+ // Only export trading splits when exporting a trading account
+ if (!is_trading_acct &&
+ (xaccAccountGetType (split_acc) == ACCT_TYPE_TRADING))
+ continue;
+
+ if (info->simple_layout)
+ {
+ // Write line in simple layout, equivalent to a single line register view
+ auto line = make_simple_trans_line (trans, split);
+ info->failed = !gnc_csv_add_line (ss, line, info->use_quotes,
+ info->separator_str);
+ continue;
+ }
+
+ // Write complex Transaction Line.
+ auto line = make_complex_trans_line (trans, split);
+ info->failed = !gnc_csv_add_line (ss, line, info->use_quotes,
+ info->separator_str);
+
+ /* Loop through the list of splits for the Transaction */
+ for (auto node = xaccTransGetSplitList (trans); !info->failed && node;
+ node = node->next)
+ {
+ auto t_split{static_cast<Split*>(node->data)};
+
+ // base split is already written on the trans_line
+ if (split == t_split)
+ continue;
+
+ // Only export trading splits if exporting a trading account
+ Account *tsplit_acc = xaccSplitGetAccount (t_split);
+ if (!is_trading_acct &&
+ (xaccAccountGetType (tsplit_acc) == ACCT_TYPE_TRADING))
+ continue;
+
+ // Write complex Split Line.
+ auto line = make_complex_trans_line (trans, t_split);
+ info->failed = !gnc_csv_add_line (ss, line, info->use_quotes,
+ info->separator_str);
+ }
+ }
+
+ if (info->export_type == XML_EXPORT_TRANS)
+ qof_query_destroy (info->query);
+}
+
+
+/*******************************************************
+ * csv_transactions_export
+ *
+ * write a list of transactions to a text file
+ *******************************************************/
+void csv_transactions_export (CsvExportInfo *info)
+{
+ ENTER("");
+ DEBUG("File name is : %s", info->file_name);
+
+ /* Open File for writing */
+ auto ss{std::ofstream (info->file_name, std::ofstream::out)};
+
+ StringVec headers;
+ bool num_action = qof_book_use_split_action_for_num_field (gnc_get_current_book());
+
+ /* Header string */
+ if (info->simple_layout)
+ {
+ /* Translators: The following symbols will build the header
+ line of exported CSV files: */
+ headers = {
+ _("Date"),
+ _("Account Name"),
+ (num_action ? _("Transaction Number") : _("Number")),
+ _("Description"),
+ _("Full Category Path"),
+ _("Reconcile"),
+ _("Amount With Sym"),
+ _("Amount Num."),
+ _("Value With Sym"),
+ _("Value Num."),
+ _("Rate/Price"),
+ };
+ }
+ else
+ headers = {
+ _("Date"),
+ _("Transaction ID"),
+ (num_action ? _("Transaction Number") : _("Number")),
+ _("Description"),
+ _("Notes"),
+ _("Commodity/Currency"),
+ _("Void Reason"),
+ (num_action ? _("Number/Action") : _("Action")),
+ _("Memo"),
+ _("Full Account Name"),
+ _("Account Name"),
+ _("Amount With Sym"),
+ _("Amount Num."),
+ _("Value With Sym"),
+ _("Value Num."),
+ _("Reconcile"),
+ _("Reconcile Date"),
+ _("Rate/Price"),
+ };
+
+ /* Write header line */
+ info->failed = !gnc_csv_add_line (ss, headers, info->use_quotes, info->separator_str);
+
+ /* Go through list of accounts */
+ TransSet trans_set;
+ for (auto ptr = info->csva.account_list; !info->failed && ptr;
+ ptr = g_list_next(ptr))
+ {
+ auto acc{static_cast<Account*>(ptr->data)};
+ DEBUG("Account being processed is : %s", xaccAccountGetName (acc));
+ account_splits (info, acc, ss, trans_set);
+ info->failed = ss.fail();
+ }
+
+ LEAVE("");
+}
+
diff --git a/gnucash/import-export/csv-exp/csv-transactions-export.h b/gnucash/import-export/csv-exp/csv-transactions-export.h
index 7583577ff3..c7718ea73e 100644
--- a/gnucash/import-export/csv-exp/csv-transactions-export.h
+++ b/gnucash/import-export/csv-exp/csv-transactions-export.h
@@ -30,10 +30,18 @@
#include "assistant-csv-export.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/** The csv_transactions_export() will let the user export the
* transactions to a delimited file.
*/
void csv_transactions_export (CsvExportInfo *info);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 0f9b448fd2..6ddf60c869 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -327,7 +327,7 @@ gnucash/import-export/bi-import/dialog-bi-import-helper.c
gnucash/import-export/bi-import/gnc-plugin-bi-import.c
gnucash/import-export/csv-exp/assistant-csv-export.c
gnucash/import-export/csv-exp/csv-export-helpers.cpp
-gnucash/import-export/csv-exp/csv-transactions-export.c
+gnucash/import-export/csv-exp/csv-transactions-export.cpp
gnucash/import-export/csv-exp/csv-tree-export.cpp
gnucash/import-export/csv-exp/gnc-plugin-csv-export.c
gnucash/import-export/csv-imp/assistant-csv-account-import.c
commit cb8cdd1b9930070cb9ddaf27661d14a62b25f7d1
Author: Christopher Lam <christopher.lck at gmail.com>
Date: Sat Apr 1 13:51:51 2023 +0800
[csv-tree-export.cpp] convert to cpp, use fstream
diff --git a/gnucash/import-export/csv-exp/CMakeLists.txt b/gnucash/import-export/csv-exp/CMakeLists.txt
index 61ae73e292..1720703524 100644
--- a/gnucash/import-export/csv-exp/CMakeLists.txt
+++ b/gnucash/import-export/csv-exp/CMakeLists.txt
@@ -5,7 +5,7 @@ set(csv_export_SOURCES
gnc-plugin-csv-export.c
csv-export-helpers.cpp
assistant-csv-export.c
- csv-tree-export.c
+ csv-tree-export.cpp
csv-transactions-export.c
)
diff --git a/gnucash/import-export/csv-exp/csv-tree-export.c b/gnucash/import-export/csv-exp/csv-tree-export.c
deleted file mode 100644
index a8ad62e064..0000000000
--- a/gnucash/import-export/csv-exp/csv-tree-export.c
+++ /dev/null
@@ -1,265 +0,0 @@
-/*******************************************************************\
- * csv-tree-export.c -- Export Account Tree to a file *
- * *
- * Copyright (C) 2012 Robert Fewell *
- * *
- * This program is free software; you can redistribute it and/or *
- * modify it under the terms of the GNU General Public License as *
- * published by the Free Software Foundation; either version 2 of *
- * the License, or (at your option) any later version. *
- * *
- * This program is distributed in the hope that it will be useful, *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
- * GNU General Public License for more details. *
- * *
- * You should have received a copy of the GNU General Public License*
- * along with this program; if not, contact: *
- * *
- * Free Software Foundation Voice: +1-617-542-5942 *
- * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
- * Boston, MA 02110-1301, USA gnu at gnu.org *
-\********************************************************************/
-/** @file csv-tree-export.c
- @brief CSV Export Account Tree
- @author Copyright (c) 2012 Robert Fewell
-*/
-#include <config.h>
-
-#include <gtk/gtk.h>
-#include <glib/gi18n.h>
-#include <glib/gstdio.h>
-
-#include "gnc-commodity.h"
-#include "gnc-ui-util.h"
-
-#include "csv-tree-export.h"
-
-/* This static indicates the debugging module that this .o belongs to. */
-static QofLogModule log_module = GNC_MOD_ASSISTANT;
-
-/* CSV spec requires CRLF line endings. Tweak the end-of-line string so this
- * true for each platform */
-#ifdef G_OS_WIN32
-# define EOLSTR "\n"
-#else
-# define EOLSTR "\r\n"
-#endif
-
-/******************************************************************/
-
-/*******************************************************
- * write_line_to_file
- *
- * write a text string to a file pointer, return TRUE if
- * successful.
- *******************************************************/
-static
-gboolean write_line_to_file (FILE *fh, char * line)
-{
- int len, written;
- DEBUG("Account String: %s", line);
-
- /* Write account line */
- len = strlen (line);
- written = fwrite (line, 1, len, fh);
-
- if (written != len)
- return FALSE;
- else
- return TRUE;
-}
-
-/*******************************************************
- * csv_test_field_string
- *
- * Test the field string for ," and new lines
- *******************************************************/
-static
-gchar *csv_test_field_string (CsvExportInfo *info, const gchar *string_in)
-{
- gboolean need_quote = FALSE;
- gchar **parts;
- gchar *string_parts;
- gchar *string_out;
-
- /* Check for " and then "" them */
- parts = g_strsplit (string_in, "\"", -1);
- string_parts = g_strjoinv ("\"\"", parts);
- g_strfreev (parts);
-
- /* Check for separator string and \n and " in field,
- if so quote field if not already quoted */
- if (g_strrstr (string_parts, info->separator_str) != NULL)
- need_quote = TRUE;
- if (g_strrstr (string_parts, "\n") != NULL)
- need_quote = TRUE;
- if (g_strrstr (string_parts, "\"") != NULL)
- need_quote = TRUE;
-
- if (!info->use_quotes && need_quote)
- string_out = g_strconcat ("\"", string_parts, "\"", NULL);
- else
- string_out = g_strdup (string_parts);
-
- g_free (string_parts);
- return string_out;
-}
-
-/*******************************************************
- * csv_tree_export
- *
- * write a list of accounts settings to a text file
- *******************************************************/
-void csv_tree_export (CsvExportInfo *info)
-{
- FILE *fh;
- Account *root;
- Account *acc;
- GList *accts, *ptr;
-
- ENTER("");
- DEBUG("File name is : %s", info->file_name);
-
- /* Get list of Accounts */
- root = gnc_book_get_root_account (gnc_get_current_book());
- accts = gnc_account_get_descendants_sorted (root);
- info->failed = FALSE;
-
- /* Open File for writing */
- fh = g_fopen (info->file_name, "w");
- if (fh != NULL)
- {
- gchar *header;
- gchar *part1;
- gchar *part2;
- const gchar *currentSel;
- gchar *end_sep;
- gchar *mid_sep;
- int i;
-
-
- /* Set up separators */
- if (info->use_quotes)
- {
- end_sep = "\"";
- mid_sep = g_strconcat ("\"", info->separator_str, "\"", NULL);
- }
- else
- {
- end_sep = "";
- mid_sep = g_strconcat (info->separator_str, NULL);
- }
-
- /* Header string, 'eol = end of line marker' */
- header = g_strconcat (end_sep, _("Type"), mid_sep, _("Full Account Name"), mid_sep, _("Account Name"), mid_sep,
- _("Account Code"), mid_sep, _("Description"), mid_sep, _("Account Color"), mid_sep,
- _("Notes"), mid_sep, _("Symbol"), mid_sep, _("Namespace"), mid_sep,
- _("Hidden"), mid_sep, _("Tax Info"), mid_sep, _("Placeholder"), end_sep, EOLSTR, NULL);
- DEBUG("Header String: %s", header);
-
- /* Write header line */
- if (!write_line_to_file (fh, header))
- {
- info->failed = TRUE;
- g_free (mid_sep);
- g_free (header);
- return;
- }
- g_free (header);
-
- /* Go through list of accounts */
- for (ptr = accts, i = 0; ptr; ptr = g_list_next (ptr), i++)
- {
- gchar *fullname = NULL;
- gchar *str_temp = NULL;
- acc = ptr->data;
- DEBUG("Account being processed is : %s", xaccAccountGetName (acc));
- /* Type */
- currentSel = xaccAccountTypeEnumAsString (xaccAccountGetType (acc));
- part1 = g_strconcat (end_sep, currentSel, mid_sep, NULL);
- /* Full Name */
- fullname = gnc_account_get_full_name (acc);
- str_temp = csv_test_field_string (info, fullname);
- part2 = g_strconcat (part1, str_temp, mid_sep, NULL);
- g_free (str_temp);
- g_free (fullname);
- g_free (part1);
- /* Name */
- currentSel = xaccAccountGetName (acc);
- str_temp = csv_test_field_string (info, currentSel);
- part1 = g_strconcat (part2, str_temp, mid_sep, NULL);
- g_free (str_temp);
- g_free (part2);
- /* Code */
- currentSel = xaccAccountGetCode (acc) ? xaccAccountGetCode (acc) : "";
- str_temp = csv_test_field_string (info, currentSel);
- part2 = g_strconcat (part1, str_temp, mid_sep, NULL);
- g_free (str_temp);
- g_free (part1);
- /* Description */
- currentSel = xaccAccountGetDescription (acc) ? xaccAccountGetDescription (acc) : "";
- str_temp = csv_test_field_string (info, currentSel);
- part1 = g_strconcat (part2, str_temp, mid_sep, NULL);
- g_free (str_temp);
- g_free (part2);
- /* Color */
- currentSel = xaccAccountGetColor (acc) ? xaccAccountGetColor (acc) : "" ;
- str_temp = csv_test_field_string (info, currentSel);
- part2 = g_strconcat (part1, str_temp, mid_sep, NULL);
- g_free (str_temp);
- g_free (part1);
- /* Notes */
- currentSel = xaccAccountGetNotes (acc) ? xaccAccountGetNotes (acc) : "" ;
- str_temp = csv_test_field_string (info, currentSel);
- part1 = g_strconcat (part2, str_temp, mid_sep, NULL);
- g_free (str_temp);
- g_free (part2);
- /* Commodity Symbol */
- currentSel = gnc_commodity_get_mnemonic (xaccAccountGetCommodity (acc));
- str_temp = csv_test_field_string (info, currentSel);
- part2 = g_strconcat (part1, str_temp, mid_sep, NULL);
- g_free (str_temp);
- g_free (part1);
- /* Commodity Namespace */
- currentSel = gnc_commodity_get_namespace (xaccAccountGetCommodity (acc));
- str_temp = csv_test_field_string (info, currentSel);
- part1 = g_strconcat (part2, str_temp, mid_sep, NULL);
- g_free (str_temp);
- g_free (part2);
- /* Hidden */
- currentSel = xaccAccountGetHidden (acc) ? "T" : "F" ;
- part2 = g_strconcat (part1, currentSel, mid_sep, NULL);
- g_free (part1);
- /* Tax */
- currentSel = xaccAccountGetTaxRelated (acc) ? "T" : "F" ;
- part1 = g_strconcat (part2, currentSel, mid_sep, NULL);
- g_free (part2);
- /* Place Holder / end of line marker */
- currentSel = xaccAccountGetPlaceholder (acc) ? "T" : "F" ;
- part2 = g_strconcat (part1, currentSel, end_sep, EOLSTR, NULL);
- g_free (part1);
-
- DEBUG("Account String: %s", part2);
-
- /* Write to file */
- if (!write_line_to_file (fh, part2))
- {
- info->failed = TRUE;
- break;
- }
- g_free (part2);
- }
- g_free (mid_sep);
- }
- else
- info->failed = TRUE;
- if (fh)
- fclose (fh);
-
- g_list_free (accts);
- LEAVE("");
-}
-
-
-
diff --git a/gnucash/import-export/csv-exp/csv-tree-export.cpp b/gnucash/import-export/csv-exp/csv-tree-export.cpp
new file mode 100644
index 0000000000..e52c3ba591
--- /dev/null
+++ b/gnucash/import-export/csv-exp/csv-tree-export.cpp
@@ -0,0 +1,112 @@
+/*******************************************************************\
+ * csv-tree-export.cpp -- Export Account Tree to a file *
+ * *
+ * Copyright (C) 2012 Robert Fewell *
+ * *
+ * This program is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public License as *
+ * published by the Free Software Foundation; either version 2 of *
+ * the License, or (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License*
+ * along with this program; if not, contact: *
+ * *
+ * Free Software Foundation Voice: +1-617-542-5942 *
+ * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
+ * Boston, MA 02110-1301, USA gnu at gnu.org *
+\********************************************************************/
+/** @file csv-tree-export.c
+ @brief CSV Export Account Tree
+ @author Copyright (c) 2012 Robert Fewell
+*/
+#include <config.h>
+
+#include <cstdio>
+#include <fstream>
+#include <string>
+#include <vector>
+#include <algorithm>
+
+#include "gnc-commodity.h"
+#include "gnc-ui-util.h"
+#include "csv-tree-export.h"
+#include "csv-export-helpers.hpp"
+
+/* This static indicates the debugging module that this .o belongs to. */
+static QofLogModule log_module = GNC_MOD_ASSISTANT;
+
+static std::string
+account_get_fullname_str (Account *account)
+{
+ auto name{gnc_account_get_full_name (account)};
+ auto rv{std::string(name)};
+ g_free (name);
+ return rv;
+}
+
+/*******************************************************
+ * csv_tree_export
+ *
+ * write a list of accounts settings to a text file
+ *******************************************************/
+void
+csv_tree_export (CsvExportInfo *info)
+{
+ ENTER("");
+ DEBUG("File name is : %s", info->file_name);
+
+ /* Open File for writing */
+ auto ss{std::ofstream (info->file_name, std::ofstream::out)};
+
+ /* Header string */
+ StringVec headervec = {
+ _("Type"), _("Full Account Name"), _("Account Name"),
+ _("Account Code"), _("Description"), _("Account Color"),
+ _("Notes"), _("Symbol"), _("Namespace"),
+ _("Hidden"), _("Tax Info"), _("Placeholder")
+ };
+
+ /* Write header line */
+ info->failed = ss.fail() ||
+ !gnc_csv_add_line (ss, headervec, info->use_quotes, info->separator_str);
+
+ /* Get list of Accounts */
+ auto root{gnc_book_get_root_account (gnc_get_current_book())};
+ auto accts{gnc_account_get_descendants_sorted (root)};
+ auto str_or_empty = [](const char* a){ return a ? a : ""; };
+ auto bool_to_char = [](bool b){ return b ? "T" : "F"; };
+
+ /* Go through list of accounts */
+ for (GList *ptr = accts; !info->failed && ptr; ptr = g_list_next (ptr))
+ {
+ auto acc{static_cast<Account*>(ptr->data)};
+ DEBUG("Account being processed is : %s", xaccAccountGetName (acc));
+
+ StringVec line = {
+ xaccAccountTypeEnumAsString (xaccAccountGetType (acc)),
+ account_get_fullname_str (acc),
+ xaccAccountGetName (acc),
+ str_or_empty (xaccAccountGetCode (acc)),
+ str_or_empty (xaccAccountGetDescription (acc)),
+ str_or_empty (xaccAccountGetColor (acc)),
+ str_or_empty (xaccAccountGetNotes (acc)),
+ gnc_commodity_get_mnemonic (xaccAccountGetCommodity (acc)),
+ gnc_commodity_get_namespace (xaccAccountGetCommodity (acc)),
+ bool_to_char (xaccAccountGetHidden (acc)),
+ bool_to_char (xaccAccountGetTaxRelated (acc)),
+ bool_to_char (xaccAccountGetPlaceholder (acc)),
+ };
+ info->failed = !gnc_csv_add_line (ss, line, info->use_quotes, info->separator_str);
+ }
+
+ g_list_free (accts);
+ LEAVE("");
+}
+
+
+
diff --git a/gnucash/import-export/csv-exp/csv-tree-export.h b/gnucash/import-export/csv-exp/csv-tree-export.h
index 5a7f9affe3..4c86cd1269 100644
--- a/gnucash/import-export/csv-exp/csv-tree-export.h
+++ b/gnucash/import-export/csv-exp/csv-tree-export.h
@@ -29,10 +29,18 @@
#include "assistant-csv-export.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/** The csv_tree_export() will let the user export the
* account tree to a delimited file.
*/
void csv_tree_export (CsvExportInfo *info);
+#ifdef __cplusplus
+}
+#endif
+
#endif
diff --git a/po/POTFILES.in b/po/POTFILES.in
index cc95414f1f..0f9b448fd2 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -328,7 +328,7 @@ gnucash/import-export/bi-import/gnc-plugin-bi-import.c
gnucash/import-export/csv-exp/assistant-csv-export.c
gnucash/import-export/csv-exp/csv-export-helpers.cpp
gnucash/import-export/csv-exp/csv-transactions-export.c
-gnucash/import-export/csv-exp/csv-tree-export.c
+gnucash/import-export/csv-exp/csv-tree-export.cpp
gnucash/import-export/csv-exp/gnc-plugin-csv-export.c
gnucash/import-export/csv-imp/assistant-csv-account-import.c
gnucash/import-export/csv-imp/assistant-csv-price-import.cpp
commit e6352f54979a40919388a9c0ffe982b9adfbe33f
Author: Christopher Lam <christopher.lck at gmail.com>
Date: Sat Apr 1 13:51:14 2023 +0800
[csv-export-helpers.cpp] helper function and full test suite
diff --git a/gnucash/import-export/csv-exp/CMakeLists.txt b/gnucash/import-export/csv-exp/CMakeLists.txt
index be95550015..61ae73e292 100644
--- a/gnucash/import-export/csv-exp/CMakeLists.txt
+++ b/gnucash/import-export/csv-exp/CMakeLists.txt
@@ -1,5 +1,9 @@
+
+add_subdirectory(test)
+
set(csv_export_SOURCES
gnc-plugin-csv-export.c
+ csv-export-helpers.cpp
assistant-csv-export.c
csv-tree-export.c
csv-transactions-export.c
@@ -11,6 +15,7 @@ set_source_files_properties (${csv_export_SOURCES} PROPERTIES OBJECT_DEPENDS ${C
set(csv_export_noinst_HEADERS
gnc-plugin-csv-export.h
assistant-csv-export.h
+ csv-export-helpers.hpp
csv-tree-export.h
csv-transactions-export.h
)
diff --git a/gnucash/import-export/csv-exp/csv-export-helpers.cpp b/gnucash/import-export/csv-exp/csv-export-helpers.cpp
new file mode 100644
index 0000000000..f39953636b
--- /dev/null
+++ b/gnucash/import-export/csv-exp/csv-export-helpers.cpp
@@ -0,0 +1,84 @@
+/*******************************************************************\
+ * csv-export-helpers.c -- Functions to assist csv export *
+ * *
+ * This program is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public License as *
+ * published by the Free Software Foundation; either version 2 of *
+ * the License, or (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License*
+ * along with this program; if not, contact: *
+ * *
+ * Free Software Foundation Voice: +1-617-542-5942 *
+ * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
+ * Boston, MA 02110-1301, USA gnu at gnu.org *
+\********************************************************************/
+/** @file csv-export-helprs.cpp
+ @brief CSV Export helper functions
+ @author Christopher Lam
+*/
+#include <config.h>
+
+#include <cstring>
+#include <cstdio>
+#include <fstream>
+#include <vector>
+
+#include "gnc-ui-util.h"
+#include "csv-export-helpers.hpp"
+
+/* This static indicates the debugging module that this .o belongs to. */
+[[maybe_unused]] static QofLogModule log_module = GNC_MOD_ASSISTANT;
+
+/* CSV spec requires CRLF line endings. Tweak the end-of-line string so this
+ * true for each platform */
+#ifdef G_OS_WIN32
+# define EOLSTR "\n"
+#else
+# define EOLSTR "\r\n"
+#endif
+
+#define QUOTE '"'
+
+bool
+gnc_csv_add_line (std::ostream& ss, const StringVec& str_vec,
+ bool use_quotes, const char* sep)
+{
+ auto first{true};
+ auto sep_view{std::string_view (sep ? sep : "")};
+ for (const auto& str : str_vec)
+ {
+ auto need_quote = use_quotes
+ || (!sep_view.empty() && str.find (sep_view) != std::string::npos)
+ || str.find_first_of ("\"\n\r") != std::string::npos;
+
+ if (first)
+ first = false;
+ else
+ ss << sep_view;
+
+ if (need_quote)
+ ss << QUOTE;
+
+ for (const char& p : str)
+ {
+ ss << p;
+ if (p == QUOTE)
+ ss << QUOTE;
+ }
+
+ if (need_quote)
+ ss << QUOTE;
+
+ if (ss.fail())
+ return false;
+ }
+ ss << EOLSTR;
+
+ return !ss.fail();
+}
diff --git a/gnucash/import-export/csv-exp/csv-export-helpers.hpp b/gnucash/import-export/csv-exp/csv-export-helpers.hpp
new file mode 100644
index 0000000000..be8fc24dc8
--- /dev/null
+++ b/gnucash/import-export/csv-exp/csv-export-helpers.hpp
@@ -0,0 +1,39 @@
+/*******************************************************************\
+ * This program is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public License as *
+ * published by the Free Software Foundation; either version 2 of *
+ * the License, or (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License*
+ * along with this program; if not, contact: *
+ * *
+ * Free Software Foundation Voice: +1-617-542-5942 *
+ * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
+ * Boston, MA 02110-1301, USA gnu at gnu.org *
+\********************************************************************/
+
+#ifndef CSV_EXPORT_HELPERS
+#define CSV_EXPORT_HELPERS
+
+#include <string>
+#include <cstdio>
+#include <fstream>
+#include <vector>
+
+using StringVec = std::vector<std::string>;
+
+// add a csv-formatted line onto output stream. charsvec is the vector
+// of std::strings, sep is the separator string. use_quotes to always
+// "quote"; some strings may be quoted anyway if contains separator
+// string, quote, \r or \n. This function returns a bool indicating
+// success.
+bool gnc_csv_add_line (std::ostream& ss, const StringVec& charsvec,
+ bool use_quotes, const char* sep);
+
+#endif
+
diff --git a/gnucash/import-export/csv-exp/test/CMakeLists.txt b/gnucash/import-export/csv-exp/test/CMakeLists.txt
new file mode 100644
index 0000000000..6689091025
--- /dev/null
+++ b/gnucash/import-export/csv-exp/test/CMakeLists.txt
@@ -0,0 +1,25 @@
+
+set (test-csv-export-helpers_SOURCES
+ test-csv-export-helpers.cpp
+)
+
+set (test-csv-export-helpers_INCLUDE_DIRS
+ ${CMAKE_BINARY_DIR}/common
+ ${CMAKE_SOURCE_DIR}/libgnucash/engine
+)
+
+set (test-csv-export-helpers_LIBS
+ gnc-csv-export
+ gtest
+)
+
+gnc_add_test (test-csv-export-helpers
+ "${test-csv-export-helpers_SOURCES}"
+ test-csv-export-helpers_INCLUDE_DIRS
+ test-csv-export-helpers_LIBS
+)
+
+set_dist_list (test-csv-export_DIST
+ CMakeLists.txt
+ ${test-csv-export-helpers_SOURCES}
+)
diff --git a/gnucash/import-export/csv-exp/test/test-csv-export-helpers.cpp b/gnucash/import-export/csv-exp/test/test-csv-export-helpers.cpp
new file mode 100644
index 0000000000..1cafe5a8b3
--- /dev/null
+++ b/gnucash/import-export/csv-exp/test/test-csv-export-helpers.cpp
@@ -0,0 +1,92 @@
+/********************************************************************
+ * This program is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public License as *
+ * published by the Free Software Foundation; either version 2 of *
+ * the License, or (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License*
+ * along with this program; if not, you can retrieve it from *
+ * https://www.gnu.org/licenses/old-licenses/gpl-2.0.html *
+ * or contact: *
+ * *
+ * Free Software Foundation Voice: +1-617-542-5942 *
+ * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
+ * Boston, MA 02110-1301, USA gnu at gnu.org *
+ ********************************************************************/
+
+// #include "config.h"
+#include "csv-export-helpers.hpp"
+#include <gtest/gtest.h>
+
+#include <sstream>
+
+#ifdef G_OS_WIN32
+# define EOLSTR "\n"
+#else
+# define EOLSTR "\r\n"
+#endif
+
+TEST (CsvHelperTest, EmptyTests)
+{
+ std::ostringstream ss;
+ gnc_csv_add_line (ss, {}, false, nullptr);
+ ASSERT_EQ (ss.str(), EOLSTR);
+
+ std::ostringstream().swap(ss);
+ gnc_csv_add_line (ss, {}, true, ",");
+ ASSERT_EQ (ss.str(), EOLSTR);
+
+ std::ostringstream().swap(ss);
+ gnc_csv_add_line (ss, {}, false, ",");
+ ASSERT_EQ (ss.str(), EOLSTR);
+
+ std::ostringstream().swap(ss);
+ gnc_csv_add_line (ss, {}, true, nullptr);
+ ASSERT_EQ (ss.str(), EOLSTR);
+}
+
+TEST (CsvHelperTest, BasicTests)
+{
+ std::ostringstream ss;
+ gnc_csv_add_line (ss, { "A","B","C","","D" }, false, ",");
+ ASSERT_EQ (ss.str(), "A,B,C,,D" EOLSTR);
+
+ std::ostringstream().swap(ss);
+ gnc_csv_add_line (ss, { "A","B","C","","D" }, false, "");
+ ASSERT_EQ (ss.str(), "ABCD" EOLSTR);
+
+ std::ostringstream().swap(ss);
+ gnc_csv_add_line (ss, { "A","B","C","","D" }, false, nullptr);
+ ASSERT_EQ (ss.str(), "ABCD" EOLSTR);
+
+ std::ostringstream().swap(ss);
+ gnc_csv_add_line (ss, { "A","B","C","","D" }, false, ";");
+ ASSERT_EQ (ss.str(), "A;B;C;;D" EOLSTR);
+
+ std::ostringstream().swap(ss);
+ gnc_csv_add_line (ss, { "A","B","C","","D" }, true, ",");
+ ASSERT_EQ (ss.str(), "\"A\",\"B\",\"C\",\"\",\"D\"" EOLSTR);
+
+}
+
+
+TEST (CsvHelperTest, ForcedQuote)
+{
+ std::ostringstream ss;
+ gnc_csv_add_line (ss, { "A","B","C","\"","D" }, false, ",");
+ ASSERT_EQ (ss.str(), "A,B,C,\"\"\"\",D" EOLSTR);
+
+ std::ostringstream().swap(ss);
+ gnc_csv_add_line (ss, { "A","B","C","",",D" }, false, ",");
+ ASSERT_EQ (ss.str(), "A,B,C,,\",D\"" EOLSTR);
+
+ std::ostringstream().swap(ss);
+ gnc_csv_add_line (ss, { "A","B","C","\n","D\r" }, false, ";");
+ ASSERT_EQ (ss.str(), "A;B;C;\"\n\";\"D\r\"" EOLSTR);
+
+}
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 2e8342a4d6..cc95414f1f 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -326,6 +326,7 @@ gnucash/import-export/bi-import/dialog-bi-import-gui.c
gnucash/import-export/bi-import/dialog-bi-import-helper.c
gnucash/import-export/bi-import/gnc-plugin-bi-import.c
gnucash/import-export/csv-exp/assistant-csv-export.c
+gnucash/import-export/csv-exp/csv-export-helpers.cpp
gnucash/import-export/csv-exp/csv-transactions-export.c
gnucash/import-export/csv-exp/csv-tree-export.c
gnucash/import-export/csv-exp/gnc-plugin-csv-export.c
Summary of changes:
gnucash/import-export/csv-exp/CMakeLists.txt | 9 +-
.../import-export/csv-exp/csv-export-helpers.cpp | 84 +++
.../import-export/csv-exp/csv-export-helpers.hpp | 27 +-
.../csv-exp/csv-transactions-export.c | 592 ---------------------
.../csv-exp/csv-transactions-export.cpp | 431 +++++++++++++++
.../csv-exp/csv-transactions-export.h | 8 +
gnucash/import-export/csv-exp/csv-tree-export.c | 265 ---------
gnucash/import-export/csv-exp/csv-tree-export.cpp | 112 ++++
gnucash/import-export/csv-exp/csv-tree-export.h | 8 +
gnucash/import-export/csv-exp/test/CMakeLists.txt | 25 +
.../csv-exp/test/test-csv-export-helpers.cpp | 92 ++++
po/POTFILES.in | 5 +-
12 files changed, 785 insertions(+), 873 deletions(-)
create mode 100644 gnucash/import-export/csv-exp/csv-export-helpers.cpp
copy libgnucash/engine/qofbook.hpp => gnucash/import-export/csv-exp/csv-export-helpers.hpp (69%)
delete mode 100644 gnucash/import-export/csv-exp/csv-transactions-export.c
create mode 100644 gnucash/import-export/csv-exp/csv-transactions-export.cpp
delete mode 100644 gnucash/import-export/csv-exp/csv-tree-export.c
create mode 100644 gnucash/import-export/csv-exp/csv-tree-export.cpp
create mode 100644 gnucash/import-export/csv-exp/test/CMakeLists.txt
create mode 100644 gnucash/import-export/csv-exp/test/test-csv-export-helpers.cpp
More information about the gnucash-changes
mailing list