diff --git a/contrib/gdpdu/gdpdu-01-03-2019.dtd b/contrib/gdpdu/gdpdu-01-03-2019.dtd new file mode 100644 index 00000000000..d9a5992943c --- /dev/null +++ b/contrib/gdpdu/gdpdu-01-03-2019.dtd @@ -0,0 +1,317 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contrib/gdpdu/index.xml b/contrib/gdpdu/index.xml new file mode 100644 index 00000000000..a174e66f27a --- /dev/null +++ b/contrib/gdpdu/index.xml @@ -0,0 +1,121 @@ + + + + 1.0 + + Hier Firmenname eintragen + Hier Ort eintragen + Datentraegerüberlassung nach GDPdU vom Hier Datum eintragen + + + Cloud + + Konten.csv + Konten + Konten + + + 20210101 + 20231231 + + YYYYMMDD + + + , + + . + + ; + + + + Kontonummer + + + + Typ + + + + Volle Kontobezeichnung + + + + Kontobezeichnung + + + + Kontonummer + + + + Beschreibung + + + + Kontofarbe + + + + Bemerkung + + + + Symbol + + + + Namensraum + + + + Versteckt + + + + Steuerrelevante Information + + + + Platzhalter + + + +
+ + Buchungen.csv + Buchungsstapel + Alle Buchungen + + , + + . + + ; + + + Datum + + DD.MM.YYYY + + + + Beschreibung + + + + Wert + + + + Sollkonto + Konten + + + Habenkonto + Konten + + +
+
+
\ No newline at end of file diff --git a/gnucash/gtkbuilder/assistant-csv-export.glade b/gnucash/gtkbuilder/assistant-csv-export.glade index bd405dc0693..8427aa99c8c 100644 --- a/gnucash/gtkbuilder/assistant-csv-export.glade +++ b/gnucash/gtkbuilder/assistant-csv-export.glade @@ -90,6 +90,21 @@ Select the type of Export required and the separator that will be used. 0 + + + GdPDU + True + True + False + start + True + + + + 2 + 0 + + diff --git a/gnucash/import-export/csv-exp/CMakeLists.txt b/gnucash/import-export/csv-exp/CMakeLists.txt index 5ff28385d79..5dc2eecadd8 100644 --- a/gnucash/import-export/csv-exp/CMakeLists.txt +++ b/gnucash/import-export/csv-exp/CMakeLists.txt @@ -7,6 +7,7 @@ set(csv_export_SOURCES assistant-csv-export.c csv-tree-export.cpp csv-transactions-export.cpp + csv-transaction-export-line.cpp ) # Add dependency on config.h @@ -18,6 +19,7 @@ set(csv_export_noinst_HEADERS csv-export-helpers.hpp csv-tree-export.h csv-transactions-export.h + csv-transaction-export-line.hpp ) add_library(gnc-csv-export ${csv_export_noinst_HEADERS} ${csv_export_SOURCES}) diff --git a/gnucash/import-export/csv-exp/assistant-csv-export.c b/gnucash/import-export/csv-exp/assistant-csv-export.c index 89fab55db0b..4bacafb5b7c 100644 --- a/gnucash/import-export/csv-exp/assistant-csv-export.c +++ b/gnucash/import-export/csv-exp/assistant-csv-export.c @@ -66,6 +66,7 @@ void csv_export_assistant_summary_page_prepare (GtkAssistant *assistant, gpointe void csv_export_quote_cb (GtkToggleButton *button, gpointer user_data); void csv_export_simple_cb (GtkToggleButton *button, gpointer user_data); +void csv_export_gdpdu_cb (GtkToggleButton *button, gpointer user_data); void csv_export_sep_cb (GtkWidget *radio, gpointer user_data); void csv_export_custom_entry_cb (GtkWidget *widget, gpointer user_data); @@ -104,6 +105,7 @@ static const gchar *start_trans_simple_string = N_( "in a register in 'Basic Ledger' mode. As such some transfer detail " "could be lost."); + static const gchar *finish_tree_string = N_( /* Translators: %s is the file name. */ "The account tree will be exported to the file '%s' when you click \"Apply\".\n\n" @@ -272,6 +274,31 @@ csv_export_simple_cb (GtkToggleButton *button, gpointer user_data) gtk_label_set_text (GTK_LABEL(info->start_label), msg); g_free (msg); + + gtk_widget_set_sensitive(info->comma_radio, !info->gdpdu_layout); + gtk_widget_set_sensitive(info->colon_radio, !info->gdpdu_layout); + gtk_widget_set_sensitive(info->semicolon_radio, !info->gdpdu_layout); + gtk_widget_set_sensitive(info->custom_radio, !info->gdpdu_layout); +} + +/******************************************************* + * csv_export_gdpdu_cb + * + * call back for use of gdpdu_layout + *******************************************************/ +void +csv_export_gdpdu_cb (GtkToggleButton *button, gpointer user_data) +{ + CsvExportInfo *info = user_data; + + info->gdpdu_layout = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(button)); + + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(info->semicolon_radio), info->gdpdu_layout); + + gtk_widget_set_sensitive(info->comma_radio, !info->gdpdu_layout); + gtk_widget_set_sensitive(info->colon_radio, !info->gdpdu_layout); + gtk_widget_set_sensitive(info->semicolon_radio, !info->gdpdu_layout); + gtk_widget_set_sensitive(info->custom_radio, !info->gdpdu_layout); } /******************************************************* @@ -848,13 +875,12 @@ csv_export_close_handler (gpointer user_data) static GtkWidget * csv_export_assistant_create (CsvExportInfo *info) { - GtkBuilder *builder; GtkWidget *button; GtkWidget *table, *hbox; - builder = gtk_builder_new(); - gnc_builder_add_from_file (builder , "assistant-csv-export.glade", "csv_export_assistant"); - info->assistant = GTK_WIDGET(gtk_builder_get_object (builder, "csv_export_assistant")); + info->builder = gtk_builder_new(); + gnc_builder_add_from_file (info->builder , "assistant-csv-export.glade", "csv_export_assistant"); + info->assistant = GTK_WIDGET(gtk_builder_get_object (info->builder, "csv_export_assistant")); // Set the name for this assistant so it can be easily manipulated with css gtk_widget_set_name (GTK_WIDGET(info->assistant), "gnc-id-assistant-csv-export"); @@ -864,22 +890,30 @@ csv_export_assistant_create (CsvExportInfo *info) load_settings (info); /* Start Page */ - info->start_page = GTK_WIDGET(gtk_builder_get_object(builder, "start_page")); - info->start_label = GTK_WIDGET(gtk_builder_get_object(builder, "start_label")); - info->custom_entry = GTK_WIDGET(gtk_builder_get_object(builder, "custom_entry")); + info->start_page = GTK_WIDGET(gtk_builder_get_object(info->builder, "start_page")); + info->start_label = GTK_WIDGET(gtk_builder_get_object(info->builder, "start_label")); + info->custom_entry = GTK_WIDGET(gtk_builder_get_object(info->builder, "custom_entry")); + info->simple_layout_check = GTK_WIDGET(gtk_builder_get_object(info->builder, "simple_layout")); + info->gdpdu_layout_check = GTK_WIDGET(gtk_builder_get_object(info->builder, "gdpdu_layout")); + info->comma_radio = GTK_WIDGET(gtk_builder_get_object(info->builder, "comma_radio")); + info->colon_radio = GTK_WIDGET(gtk_builder_get_object(info->builder, "colon_radio")); + info->semicolon_radio = GTK_WIDGET(gtk_builder_get_object(info->builder, "semicolon_radio")); + info->custom_radio = GTK_WIDGET(gtk_builder_get_object(info->builder, "custom_radio")); + gtk_widget_set_sensitive (info->custom_entry, FALSE); /* Account Page */ - info->account_page = GTK_WIDGET(gtk_builder_get_object(builder, "account_page")); + info->account_page = GTK_WIDGET(gtk_builder_get_object(info->builder, "account_page")); if ((info->export_type == XML_EXPORT_TREE) || (info->export_type == XML_EXPORT_REGISTER)) { - GtkWidget *chkbox = GTK_WIDGET(gtk_builder_get_object(builder, "simple_layout")); - // Don't provide simple export layout for search registers and General Journal if ((info->export_type == XML_EXPORT_TREE) || - (g_list_length (info->csva.account_list) == 0)) - gtk_widget_destroy (chkbox); + (g_list_length (info->csva.account_list) == 0)) { + gtk_widget_destroy (info->simple_layout_check); + gtk_widget_destroy (info->gdpdu_layout_check); + } + gtk_assistant_remove_page (GTK_ASSISTANT(info->assistant), 1); //remove accounts page } else @@ -888,8 +922,8 @@ csv_export_assistant_create (CsvExportInfo *info) GtkTreeSelection *selection; GtkWidget *box, *label; - info->csva.acct_info = GTK_WIDGET(gtk_builder_get_object (builder, "acct_info_vbox")); - info->csva.num_acct_label = GTK_WIDGET(gtk_builder_get_object (builder, "num_accounts_label")); + info->csva.acct_info = GTK_WIDGET(gtk_builder_get_object (info->builder, "acct_info_vbox")); + info->csva.num_acct_label = GTK_WIDGET(gtk_builder_get_object (info->builder, "num_accounts_label")); tree_view = gnc_tree_view_account_new (FALSE); info->csva.account_treeview = GTK_WIDGET(tree_view); @@ -900,19 +934,19 @@ csv_export_assistant_create (CsvExportInfo *info) G_CALLBACK(csv_export_account_changed_cb), info); gtk_widget_show (info->csva.account_treeview); - box = GTK_WIDGET(gtk_builder_get_object (builder, "account_scroll")); + box = GTK_WIDGET(gtk_builder_get_object (info->builder, "account_scroll")); gtk_container_add (GTK_CONTAINER(box), info->csva.account_treeview); - label = GTK_WIDGET(gtk_builder_get_object (builder, "accounts_label")); + label = GTK_WIDGET(gtk_builder_get_object (info->builder, "accounts_label")); gtk_label_set_mnemonic_widget (GTK_LABEL(label), GTK_WIDGET(tree_view)); /* select subaccounts button */ - button = GTK_WIDGET(gtk_builder_get_object (builder, "select_subaccounts_button")); + button = GTK_WIDGET(gtk_builder_get_object (info->builder, "select_subaccounts_button")); info->csva.select_button = button; g_signal_connect (G_OBJECT(button), "clicked", G_CALLBACK(csv_export_select_subaccounts_clicked_cb), info); - button = GTK_WIDGET(gtk_builder_get_object (builder, "select_all_button")); + button = GTK_WIDGET(gtk_builder_get_object (info->builder, "select_all_button")); info->csva.select_button = button; g_signal_connect (G_OBJECT(button), "clicked", G_CALLBACK(csv_export_select_all_clicked_cb), info); @@ -921,7 +955,7 @@ csv_export_assistant_create (CsvExportInfo *info) G_CALLBACK(csv_export_cursor_changed_cb), info); /* Set the date info */ - button = GTK_WIDGET(gtk_builder_get_object (builder, "show_range")); + button = GTK_WIDGET(gtk_builder_get_object (info->builder, "show_range")); /* Get the Earliest and Latest dates in Book */ get_earliest_and_latest_in_book (info, gnc_get_current_book()); @@ -930,19 +964,19 @@ csv_export_assistant_create (CsvExportInfo *info) info->csvd.end_time = info->csvd.latest_time; gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(button), FALSE); - table = GTK_WIDGET(gtk_builder_get_object (builder, "select_range_table")); + table = GTK_WIDGET(gtk_builder_get_object (info->builder, "select_range_table")); info->csvd.table = table; gtk_widget_set_sensitive (GTK_WIDGET(table), FALSE); - info->csvd.start_date_choose = GTK_WIDGET(gtk_builder_get_object (builder, "start_date_choose")); - info->csvd.start_date_today = GTK_WIDGET(gtk_builder_get_object (builder, "start_date_today")); - info->csvd.end_date_choose = GTK_WIDGET(gtk_builder_get_object (builder, "end_date_choose")); - info->csvd.end_date_today = GTK_WIDGET(gtk_builder_get_object (builder, "end_date_today")); + info->csvd.start_date_choose = GTK_WIDGET(gtk_builder_get_object (info->builder, "start_date_choose")); + info->csvd.start_date_today = GTK_WIDGET(gtk_builder_get_object (info->builder, "start_date_today")); + info->csvd.end_date_choose = GTK_WIDGET(gtk_builder_get_object (info->builder, "end_date_choose")); + info->csvd.end_date_today = GTK_WIDGET(gtk_builder_get_object (info->builder, "end_date_today")); /* Start date info */ info->csvd.start_date = gnc_date_edit_new (gnc_time (NULL), FALSE, FALSE); gtk_widget_set_sensitive (info->csvd.start_date, FALSE); - hbox = GTK_WIDGET(gtk_builder_get_object (builder, "start_date_hbox")); + hbox = GTK_WIDGET(gtk_builder_get_object (info->builder, "start_date_hbox")); gtk_box_pack_start (GTK_BOX(hbox), info->csvd.start_date, TRUE, TRUE, 0); gtk_widget_show (info->csvd.start_date); gnc_date_edit_set_time (GNC_DATE_EDIT(info->csvd.start_date), info->csvd.start_time); @@ -952,7 +986,7 @@ csv_export_assistant_create (CsvExportInfo *info) /* End date info */ info->csvd.end_date = gnc_date_edit_new (gnc_time (NULL), FALSE, FALSE); gtk_widget_set_sensitive (info->csvd.end_date, FALSE); - hbox = GTK_WIDGET(gtk_builder_get_object (builder, "end_date_hbox")); + hbox = GTK_WIDGET(gtk_builder_get_object (info->builder, "end_date_hbox")); gtk_box_pack_start (GTK_BOX(hbox), info->csvd.end_date, TRUE, TRUE, 0); gtk_widget_show (info->csvd.end_date); gnc_date_edit_set_time (GNC_DATE_EDIT(info->csvd.end_date), info->csvd.end_time); @@ -965,7 +999,7 @@ csv_export_assistant_create (CsvExportInfo *info) } /* File chooser Page */ - info->file_page = GTK_WIDGET(gtk_builder_get_object(builder, "file_page")); + info->file_page = GTK_WIDGET(gtk_builder_get_object(info->builder, "file_page")); info->file_chooser = gtk_file_chooser_widget_new (GTK_FILE_CHOOSER_ACTION_SAVE); g_signal_connect (G_OBJECT(info->file_chooser), "selection-changed", @@ -978,10 +1012,10 @@ csv_export_assistant_create (CsvExportInfo *info) gtk_widget_show (info->file_chooser); /* Finish Page */ - info->finish_label = GTK_WIDGET(gtk_builder_get_object (builder, "end_page")); + info->finish_label = GTK_WIDGET(gtk_builder_get_object (info->builder, "end_page")); /* Summary Page */ - info->summary_label = GTK_WIDGET(gtk_builder_get_object (builder, "summary_page")); + info->summary_label = GTK_WIDGET(gtk_builder_get_object (info->builder, "summary_page")); g_signal_connect (G_OBJECT(info->assistant), "destroy", G_CALLBACK(csv_export_assistant_destroy_cb), info); @@ -990,12 +1024,12 @@ csv_export_assistant_create (CsvExportInfo *info) GTK_WINDOW(info->assistant), gnc_ui_get_main_window(NULL)); if (gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_SAVE_GEOMETRY)) { - GObject *object = gtk_builder_get_object (builder, "paned"); + GObject *object = gtk_builder_get_object (info->builder, "paned"); gnc_prefs_bind (GNC_PREFS_GROUP, GNC_PREF_PANED_POS, NULL, object, "position"); } - gtk_builder_connect_signals (builder, info); - g_object_unref (G_OBJECT(builder)); + gtk_builder_connect_signals (info->builder, info); + g_object_unref (G_OBJECT(info->builder)); return info->assistant; } diff --git a/gnucash/import-export/csv-exp/assistant-csv-export.h b/gnucash/import-export/csv-exp/assistant-csv-export.h index a2cf8f299c0..5533d9d521d 100644 --- a/gnucash/import-export/csv-exp/assistant-csv-export.h +++ b/gnucash/import-export/csv-exp/assistant-csv-export.h @@ -74,6 +74,7 @@ typedef struct Query *query; + GtkBuilder *builder; GtkWidget *start_page; GtkWidget *account_page; GtkWidget *file_page; @@ -81,6 +82,13 @@ typedef struct GtkWidget *assistant; GtkWidget *start_label; GtkWidget *custom_entry; + GtkWidget *comma_radio; + GtkWidget *colon_radio; + GtkWidget *semicolon_radio; + GtkWidget *custom_radio; + GtkWidget *simple_layout_check; + GtkWidget *gdpdu_layout_check; + GtkWidget *file_chooser; GtkWidget *finish_label; @@ -92,6 +100,7 @@ typedef struct char *separator_str; gboolean use_quotes; gboolean simple_layout; + gboolean gdpdu_layout; gboolean use_custom; gboolean failed; diff --git a/gnucash/import-export/csv-exp/csv-export-helpers.cpp b/gnucash/import-export/csv-exp/csv-export-helpers.cpp index a945881ac81..ed09cf773d1 100644 --- a/gnucash/import-export/csv-exp/csv-export-helpers.cpp +++ b/gnucash/import-export/csv-exp/csv-export-helpers.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include "gnc-ui-util.h" @@ -38,56 +39,61 @@ /* 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" +#define EOLSTR "\n" #else -# define EOLSTR "\r\n" +#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) +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) + ss.exceptions(std::ios::failbit | std::ios::badbit); + try { - 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; + 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 (need_quote) - ss << QUOTE; + if (first) + first = false; + else + ss << sep_view; - for (const char& p : str) - { - ss << p; - if (p == QUOTE) + if (need_quote) ss << QUOTE; - } - if (need_quote) - ss << QUOTE; + for (const char &p : str) + { + ss << p; + if (p == QUOTE) + ss << QUOTE; + } - if (ss.fail()) - return false; + if (need_quote) + ss << QUOTE; + + if (ss.fail()) + return false; + } + ss << EOLSTR; + } + catch (const std::exception &e) + { + std::cerr << e.what() << '\n'; } - ss << EOLSTR; return !ss.fail(); } std::string -account_get_fullname_str (Account *account) +account_get_fullname_str(Account *account) { - auto name{gnc_account_get_full_name (account)}; + auto name{gnc_account_get_full_name(account)}; auto rv{std::string(name)}; - g_free (name); + g_free(name); return rv; } diff --git a/gnucash/import-export/csv-exp/csv-transaction-export-line.cpp b/gnucash/import-export/csv-exp/csv-transaction-export-line.cpp new file mode 100644 index 00000000000..2af530bbc5b --- /dev/null +++ b/gnucash/import-export/csv-exp/csv-transaction-export-line.cpp @@ -0,0 +1,458 @@ +/*******************************************************************\ + * csv-transactions-export-line.cpp -- Convert Transaction to line * + * * + * Copyright (C) 2025 Johannes Triegel * + * * + * 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@gnu.org * +\********************************************************************/ +/** @file csv-transactions-export.h + @brief CSV Export Transactions + @author Copyright (c) 2025 Johannes Triegel +*/ + +#include + +#include +#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-transaction-export-line.hpp" + +CsvTransactionExportLine::CsvTransactionExportLine(Split *split, + Transaction *transaction, + const char *separator, + bool use_quotes, + bool simple, + bool gdpdu, + bool is_trading_acc, + std::ofstream &ss) : m_split(split), + m_transaction(transaction), + m_separator(separator), + m_use_quotes(use_quotes), + m_simple(simple && !gdpdu), + m_gdpdu(gdpdu), + m_is_trading_acc(is_trading_acc), + m_ss(ss), + m_is_debit_split(false), + m_is_credit_split(false), + m_base_split(NULL), + m_base_split_account(NULL), + m_gdpdu_failed(false) +{ + GList *splits = xaccTransGetSplitList(m_transaction); + uint debit = 0; + uint credit = 0; + + if (g_list_length(splits) > 2) + { + /* Check if the base is a debit or credit booking, e.g. which is the account everything comes to or from */ + for (GList *split = splits; split != NULL; split = split->next) + { + gnc_numeric amount = xaccSplitGetAmount(static_cast(split->data)); + if (gnc_numeric_positive_p(amount)) + { + debit++; + } + else if (gnc_numeric_negative_p(amount)) + { + credit++; + } + } + if (debit > 1 && credit > 1) + { + /* If you have a booking with multiple credit and debit accounts this cannot be exported without an extra account*/ + m_gdpdu_failed = true; + return; + } + else if (credit > 1) + { + m_is_debit_split = true; + } + else + { + m_is_credit_split = true; + } + + // Find the base_split + Split *m_base_split = NULL; + for (GList *split = splits; split != NULL; split = split->next) + { + gnc_numeric amount = xaccSplitGetAmount(static_cast(split->data)); + if (gnc_numeric_positive_p(amount) && m_is_debit_split) + { + m_base_split = static_cast(split->data); + break; + } + else if (gnc_numeric_negative_p(amount) && m_is_credit_split) + { + m_base_split = static_cast(split->data); + break; + } + } + m_base_split_account = xaccSplitGetAccount(m_base_split); + } + else + { + m_is_debit_split = false; + m_is_credit_split = false; + m_base_split_account = NULL; + m_base_split = NULL; + } +} + +bool CsvTransactionExportLine::is_split_transaction() +{ + return m_is_debit_split || m_is_credit_split; +} + +std::string +CsvTransactionExportLine::get_date(Transaction *trans) +{ + char datebuff[MAX_DATE_LENGTH + 1]; + qof_print_date_buff(datebuff, MAX_DATE_LENGTH, xaccTransGetDate(m_transaction)); + return datebuff; +} + +std::string +CsvTransactionExportLine::get_guid(Transaction *trans) +{ + return gnc::GUID(*qof_entity_get_guid(QOF_INSTANCE(m_transaction))).to_string(); +} + +// Reconcile Date +std::string +CsvTransactionExportLine::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 +std::string +CsvTransactionExportLine::get_account_name(Split *split, bool full) +{ + auto account{xaccSplitGetAccount(split)}; + return full ? account_get_fullname_str(account) : xaccAccountGetName(account); +} + +// Account ode +std::string +CsvTransactionExportLine::get_account_number(Split *split) +{ + auto account{xaccSplitGetAccount(split)}; + const gchar *code = xaccAccountGetCode(account); + return code ? code : _("Unknown"); +} + +// Account Code +std::string +CsvTransactionExportLine::get_other_account_number(Split *split) +{ + auto other{xaccSplitGetOtherSplit(split)}; + return get_account_number(other); +} + +// Number +std::string +CsvTransactionExportLine::get_number(Transaction *trans) +{ + auto num{xaccTransGetNum(m_transaction)}; + return (num ? num : ""); +} + +// Description +std::string +CsvTransactionExportLine::get_description(Transaction *trans) +{ + auto desc{xaccTransGetDescription(m_transaction)}; + return (desc ? desc : ""); +} + +// Notes +std::string +CsvTransactionExportLine::get_notes(Transaction *trans) +{ + auto notes{xaccTransGetNotes(m_transaction)}; + return (notes ? notes : ""); +} + +// Void reason +std::string +CsvTransactionExportLine::get_void_reason(Transaction *trans) +{ + auto void_reason{xaccTransGetVoidReason(m_transaction)}; + return (void_reason ? void_reason : ""); +} + +// Memo +std::string +CsvTransactionExportLine::get_memo(Split *split) +{ + auto memo{xaccSplitGetMemo(split)}; + return (memo ? memo : ""); +} + +// Full Category Path or Not +std::string +CsvTransactionExportLine::get_category(Split *split, bool full) +{ + auto other{xaccSplitGetOtherSplit(split)}; + return other ? get_account_name(other, full) : _("-- Split Transaction --"); +} + +// Action +std::string +CsvTransactionExportLine::get_action(Split *split) +{ + auto action{xaccSplitGetAction(split)}; + return (action ? action : ""); +} + +// Reconcile +std::string +CsvTransactionExportLine::get_reconcile(Split *split) +{ + auto recon{gnc_get_reconcile_str(xaccSplitGetReconcile(split))}; + return (recon ? recon : ""); +} + +// Transaction commodity +std::string +CsvTransactionExportLine::get_commodity(Transaction *trans) +{ + return gnc_commodity_get_unique_name(xaccTransGetCurrency(m_transaction)); +} + +// Amount with Symbol or not +std::string +CsvTransactionExportLine::get_amount(Split *split, bool t_void, bool symbol, bool positive) +{ + auto amt_num{t_void ? xaccSplitVoidFormerAmount(split) : xaccSplitGetAmount(split)}; + if (positive) + amt_num = gnc_numeric_abs(amt_num); + auto pinfo{gnc_split_amount_print_info(split, symbol)}; + if (!symbol) + pinfo.use_separators = 0; + return xaccPrintAmount(amt_num, pinfo); +} + +// Value with Symbol or not +std::string +CsvTransactionExportLine::get_value(Split *split, bool t_void, bool symbol) +{ + auto tcurr{xaccTransGetCurrency(m_transaction)}; + auto pai{gnc_commodity_print_info(tcurr, symbol)}; + if (!symbol) + pai.use_separators = 0; + auto amt_num{t_void ? xaccSplitVoidFormerValue(split) : xaccSplitGetValue(split)}; + return xaccPrintAmount(amt_num, pai); +} + +// Share Price / Conversion factor +std::string +CsvTransactionExportLine::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 +std::string +CsvTransactionExportLine::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)); +} + +bool CsvTransactionExportLine::print_csv() +{ + if (m_gdpdu && m_gdpdu_failed) + return false; + + if (m_simple || (!is_split_transaction() && m_gdpdu)) + { + auto amount = xaccSplitGetAmount(m_split); + /* Ignore bookings with zero value in gdpdu export*/ + if (gnc_numeric_zero_p(amount) && m_gdpdu) + return true; + + auto line = m_gdpdu ? make_gdpdu_trans_line(m_split) : make_simple_trans_line(m_split); + return gnc_csv_add_line(m_ss, line, m_use_quotes, + m_separator); + } + else if (is_split_transaction() && m_gdpdu) + { + bool ok = false; + GList *splits = xaccTransGetSplitList(m_transaction); + for (GList *split = splits; split != NULL; split = split->next) + { + Split *s = static_cast(split->data); + + auto amount = xaccSplitGetAmount(s); + /* Ignore bookings with zero value in gdpdu export*/ + if (gnc_numeric_zero_p(amount)) + continue; + + auto split_account = xaccSplitGetAccount(s); + if (!split_account) + { + continue; + } + + if (xaccAccountEqual(split_account, m_base_split_account, true)) + continue; + + auto line = make_gdpdu_trans_split_line(s); + ok = gnc_csv_add_line(m_ss, line, m_use_quotes, + m_separator); + if (!ok) + break; + } + return ok; + } + else + { + /* Loop through the list of splits for the Transaction */ + bool failed = false; + for (auto node = xaccTransGetSplitList(m_transaction); !failed && node; + node = node->next) + { + auto t_split{static_cast(node->data)}; + + // base split is already written on the trans_line + if (m_split == t_split) + continue; + + // Only export trading splits if exporting a trading account + Account *tsplit_acc = xaccSplitGetAccount(t_split); + if (!m_is_trading_acc && + (xaccAccountGetType(tsplit_acc) == ACCT_TYPE_TRADING)) + continue; + + // Write complex Split Line. + auto line = make_complex_trans_line(t_split); + failed = !gnc_csv_add_line(m_ss, line, m_use_quotes, m_separator); + } + return failed; + } +} + +StringVec +CsvTransactionExportLine::make_gdpdu_trans_split_line(Split *split) +{ + auto t_void{xaccTransGetVoidStatus(m_transaction)}; + + auto base_acc_code = xaccAccountGetCode(m_base_split_account); + if (!base_acc_code) + base_acc_code = _("Unknown"); + + return { + get_date(m_transaction), + get_number(m_transaction), + m_is_credit_split ? base_acc_code : get_account_number(split), + m_is_credit_split ? get_account_number(split) : base_acc_code, + get_description(m_transaction), + get_amount(split, t_void, false, true)}; +} + +StringVec +CsvTransactionExportLine::make_gdpdu_trans_line(Split *split) +{ + auto t_void{xaccTransGetVoidStatus(m_transaction)}; + auto amount = xaccSplitGetAmount(split); + auto account = xaccSplitGetAccount(split); + auto type = xaccAccountGetType(account); + bool pos = gnc_numeric_positive_p(amount); + + /* It is possible that other types of accounts need to be taken into account here */ + bool exchange = false; + switch (type) { + case ACCT_TYPE_EQUITY: + exchange = true; + break; + default: + break; + } + + return { + get_date(m_transaction), + get_number(m_transaction), + pos || exchange ? get_other_account_number(split) : get_account_number(split), + pos || exchange ? get_account_number(split) : get_other_account_number(split), + get_description(m_transaction), + get_amount(split, t_void, false, true)}; +} + +StringVec +CsvTransactionExportLine::make_complex_trans_line(Split *split) +{ + auto t_void{xaccTransGetVoidStatus(m_transaction)}; + return { + get_date(m_transaction), + get_guid(m_transaction), + get_number(m_transaction), + get_description(m_transaction), + get_notes(m_transaction), + get_commodity(m_transaction), + get_void_reason(m_transaction), + 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)}; +} + +StringVec +CsvTransactionExportLine::make_simple_trans_line(Split *split) +{ + auto t_void{xaccTransGetVoidStatus(m_transaction)}; + return { + get_date(m_transaction), + get_account_name(split, true), + get_number(m_transaction), + get_description(m_transaction), + 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)}; +} \ No newline at end of file diff --git a/gnucash/import-export/csv-exp/csv-transaction-export-line.hpp b/gnucash/import-export/csv-exp/csv-transaction-export-line.hpp new file mode 100644 index 00000000000..233b0fd8cf3 --- /dev/null +++ b/gnucash/import-export/csv-exp/csv-transaction-export-line.hpp @@ -0,0 +1,100 @@ +/*******************************************************************\ + * csv-transactions-export-line.h -- Convert Transaction to line * + * * + * Copyright (C) 2025 Johannes Triegel * + * * + * 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@gnu.org * +\********************************************************************/ +/** @file csv-transactions-export.h + @brief CSV Export Transactions + @author Copyright (c) 2025 Johannes Triegel +*/ + +#ifndef CSV_TRANSACTIONS_EXPORT_LINE +#define CSV_TRANSACTIONS_EXPORT_LINE + +#include +#include + +#include "Transaction.h" +#include "csv-export-helpers.hpp" + +class CsvTransactionExportLine +{ +public: + CsvTransactionExportLine(Split *split, + Transaction *transaction, + const char *separator, + bool use_quotes, + bool simple, + bool gdpdu, + bool is_trading_acc, + std::ofstream &ss); + + bool print_csv(); + +protected: + std::string get_date(Transaction *trans); + std::string get_guid(Transaction *trans); + std::string get_reconcile_date(Split *split); + std::string get_account_name(Split *split, bool full); + std::string get_account_number(Split *split); + std::string get_other_account_number(Split *split); + std::string get_number(Transaction *trans); + std::string get_description(Transaction *trans); + std::string get_notes(Transaction *trans); + std::string get_void_reason(Transaction *trans); + std::string get_memo(Split *split); + std::string get_category(Split *split, bool full); + std::string get_action(Split *split); + std::string get_reconcile(Split *split); + std::string get_commodity(Transaction *trans); + std::string get_amount(Split *split, bool t_void, bool symbol, bool positive = false); + std::string get_value(Split *split, bool t_void, bool symbol); + std::string get_rate(Split *split, bool t_void); + std::string get_price(Split *split, bool t_void); + + bool is_split_transaction(); + + StringVec make_simple_trans_split_line(Split *split); + StringVec make_simple_trans_line(Split *split); + StringVec make_gdpdu_trans_split_line(Split *split); + StringVec make_gdpdu_trans_line(Split *split); + StringVec make_complex_trans_line(Split *split); + +private: + Split *m_split; + Transaction *m_transaction; + + const char *m_separator; + bool m_use_quotes; + bool m_simple; + bool m_gdpdu; + bool m_is_trading_acc; + std::ofstream &m_ss; + + bool m_is_debit_split; + bool m_is_credit_split; + + Split *m_base_split; + Account *m_base_split_account; + + bool m_gdpdu_failed; +}; + +#endif \ No newline at end of file diff --git a/gnucash/import-export/csv-exp/csv-transactions-export.cpp b/gnucash/import-export/csv-exp/csv-transactions-export.cpp index 74515e5b4bf..f70ceb690a5 100644 --- a/gnucash/import-export/csv-exp/csv-transactions-export.cpp +++ b/gnucash/import-export/csv-exp/csv-transactions-export.cpp @@ -42,217 +42,14 @@ #include "guid.hpp" #include "csv-transactions-export.h" -#include "csv-export-helpers.hpp" +#include "csv-transaction-export-line.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)}; - return full ? account_get_fullname_str (account) : 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) -{ - auto other{xaccSplitGetOtherSplit(split)}; - return other ? get_account_name (other, full) : _("-- Split Transaction --"); -} - -// 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)}; - auto pinfo{gnc_split_amount_print_info (split, symbol)}; - if (!symbol) - pinfo.use_separators = 0; - return xaccPrintAmount (amt_num, pinfo); -} - -// 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)}; - if (!symbol) - pai.use_separators = 0; - 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; +using TransSet = std::unordered_set; /******************************************************* * account_splits @@ -261,85 +58,58 @@ using TransSet = std::unordered_set; * send them to a file *******************************************************/ static void -export_query_splits (CsvExportInfo *info, bool is_trading_acct, - std::ofstream& ss, TransSet& trans_set) +export_query_splits(CsvExportInfo *info, bool is_trading_acct, + std::ofstream &ss, TransSet &trans_set) { - g_return_if_fail (info); + g_return_if_fail(info); /* Run the query */ - for (GList *splits = qof_query_run (info->query); !info->failed && splits; + for (GList *splits = qof_query_run(info->query); /*!info->failed && */ splits; splits = splits->next) { - auto split{static_cast(splits->data)}; - auto trans{xaccSplitGetParent (split)}; + auto split{static_cast(splits->data)}; + auto trans{xaccSplitGetParent(split)}; // Look for trans already exported in trans_set - if (!trans_set.emplace (trans).second) + if (!trans_set.emplace(trans).second) + { continue; + } // Look for blank split - Account *split_acc = xaccSplitGetAccount (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) + (xaccAccountGetType(split_acc) == ACCT_TYPE_TRADING)) { - // 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(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); - } + CsvTransactionExportLine line(split, trans, info->separator_str, info->use_quotes, info->simple_layout, info->gdpdu_layout, is_trading_acct, ss); + info->failed = !line.print_csv(); } } static void -account_splits (CsvExportInfo *info, Account *acc, - std::ofstream& ss, TransSet& trans_set) +account_splits(CsvExportInfo *info, Account *acc, + std::ofstream &ss, TransSet &trans_set) { - g_return_if_fail (info && GNC_IS_ACCOUNT (acc)); + g_return_if_fail(info && GNC_IS_ACCOUNT(acc)); // Setup the query for normal transaction export - auto p1 = g_slist_prepend (g_slist_prepend (nullptr, (gpointer)TRANS_DATE_POSTED), (gpointer)SPLIT_TRANS); - auto p2 = g_slist_prepend (nullptr, (gpointer)QUERY_DEFAULT_SORT); - info->query = qof_query_create_for (GNC_ID_SPLIT); - qof_query_set_book (info->query, gnc_get_current_book()); - qof_query_set_sort_order (info->query, p1, p2, nullptr); - xaccQueryAddSingleAccountMatch (info->query, acc, QOF_QUERY_AND); - xaccQueryAddDateMatchTT (info->query, true, info->csvd.start_time, true, info->csvd.end_time, QOF_QUERY_AND); - export_query_splits (info, xaccAccountGetType (acc) == ACCT_TYPE_TRADING, ss, trans_set); - qof_query_destroy (info->query); + auto p1 = g_slist_prepend(g_slist_prepend(nullptr, (gpointer)TRANS_DATE_POSTED), (gpointer)SPLIT_TRANS); + auto p2 = g_slist_prepend(nullptr, (gpointer)QUERY_DEFAULT_SORT); + info->query = qof_query_create_for(GNC_ID_SPLIT); + qof_query_set_book(info->query, gnc_get_current_book()); + qof_query_set_sort_order(info->query, p1, p2, nullptr); + xaccQueryAddSingleAccountMatch(info->query, acc, QOF_QUERY_AND); + xaccQueryAddDateMatchTT(info->query, true, info->csvd.start_time, true, info->csvd.end_time, QOF_QUERY_AND); + export_query_splits(info, xaccAccountGetType(acc) == ACCT_TYPE_TRADING, ss, trans_set); + qof_query_destroy(info->query); } /******************************************************* @@ -347,13 +117,13 @@ account_splits (CsvExportInfo *info, Account *acc, * * write a list of transactions to a text file *******************************************************/ -void csv_transactions_export (CsvExportInfo *info) +void csv_transactions_export(CsvExportInfo *info) { ENTER(""); DEBUG("File name is : %s", info->file_name); StringVec headers; - bool num_action = qof_book_use_split_action_for_num_field (gnc_get_current_book()); + bool num_action = qof_book_use_split_action_for_num_field(gnc_get_current_book()); /* Header string */ if (info->simple_layout) @@ -374,6 +144,20 @@ void csv_transactions_export (CsvExportInfo *info) _("Rate/Price"), }; } + /* Header string */ + if (info->gdpdu_layout) + { + /* Translators: The following symbols will build the header + line of exported CSV files for german GdPDU-Export: */ + headers = { + _("Date"), + (num_action ? _("Transaction Number") : _("Number")), + _("Debit Account"), + _("Credit Account"), + _("Description"), + _("Amount"), + }; + } else headers = { _("Date"), @@ -398,7 +182,7 @@ void csv_transactions_export (CsvExportInfo *info) /* Write header line */ auto ss{gnc_open_filestream(info->file_name)}; - info->failed = !gnc_csv_add_line (ss, headers, info->use_quotes, info->separator_str); + info->failed = !gnc_csv_add_line(ss, headers, info->use_quotes, info->separator_str); /* Go through list of accounts */ TransSet trans_set; @@ -407,16 +191,15 @@ void csv_transactions_export (CsvExportInfo *info) { case XML_EXPORT_TRANS: for (auto ptr = info->csva.account_list; !ss.fail() && ptr; ptr = g_list_next(ptr)) - account_splits (info, GNC_ACCOUNT(ptr->data), ss, trans_set); + account_splits(info, GNC_ACCOUNT(ptr->data), ss, trans_set); break; case XML_EXPORT_REGISTER: - export_query_splits (info, false, ss, trans_set); + export_query_splits(info, false, ss, trans_set); break; default: - PERR ("unknown export_type %d", info->export_type); + PERR("unknown export_type %d", info->export_type); } info->failed = ss.fail(); LEAVE(""); } - diff --git a/po/POTFILES.in b/po/POTFILES.in index 14fc124d623..f48a21eb2de 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -323,6 +323,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-transaction-export-line.cpp 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