GnuCash  5.6-150-g038405b370+
csv-transactions-export.cpp
1 /*******************************************************************\
2  * csv-actions-export.c -- Export Transactions to a file *
3  * *
4  * Copyright (C) 2012 Robert Fewell *
5  * *
6  * This program is free software; you can redistribute it and/or *
7  * modify it under the terms of the GNU General Public License as *
8  * published by the Free Software Foundation; either version 2 of *
9  * the License, or (at your option) any later version. *
10  * *
11  * This program is distributed in the hope that it will be useful, *
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14  * GNU General Public License for more details. *
15  * *
16  * You should have received a copy of the GNU General Public License*
17  * along with this program; if not, contact: *
18  * *
19  * Free Software Foundation Voice: +1-617-542-5942 *
20  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
21  * Boston, MA 02110-1301, USA gnu@gnu.org *
22 \********************************************************************/
27 #include "config.h"
28 
29 #include <glib/gstdio.h>
30 #include <stdbool.h>
31 
32 #include <string>
33 #include <unordered_set>
34 
35 #include <gnc-filepath-utils.h>
36 #include "gnc-commodity.h"
37 #include "gnc-ui-util.h"
38 #include "Query.h"
39 #include "Transaction.h"
40 #include "engine-helpers.h"
41 #include "qofbookslots.h"
42 #include "guid.hpp"
43 
45 #include "csv-export-helpers.hpp"
46 
47 /* This static indicates the debugging module that this .o belongs to. */
48 static QofLogModule log_module = GNC_MOD_ASSISTANT;
49 
50 
51 /*******************************************************************/
52 
53 /******************** Helper functions *********************/
54 
55 static std::string
56 get_date (Transaction *trans)
57 {
58  char datebuff [MAX_DATE_LENGTH + 1];
60  return datebuff;
61 }
62 
63 
64 static std::string
65 get_guid (Transaction *trans)
66 {
67  return gnc::GUID (*qof_entity_get_guid (QOF_INSTANCE (trans))).to_string();
68 }
69 
70 // Reconcile Date
71 static std::string
72 get_reconcile_date (Split *split)
73 {
74  if (xaccSplitGetReconcile (split) != YREC)
75  return "";
76 
77  char datebuff[MAX_DATE_LENGTH + 1];
79  return datebuff;
80 }
81 
82 // Account Name short or Long
83 static std::string
84 get_account_name (Split *split, bool full)
85 {
86  auto account{xaccSplitGetAccount (split)};
87  return full ? account_get_fullname_str (account) : xaccAccountGetName (account);
88 }
89 
90 // Number
91 static std::string
92 get_number (Transaction *trans)
93 {
94  auto num{xaccTransGetNum (trans)};
95  return (num ? num : "");
96 }
97 
98 // Description
99 static std::string
100 get_description (Transaction *trans)
101 {
102  auto desc{xaccTransGetDescription (trans)};
103  return (desc ? desc : "");
104 }
105 
106 // Notes
107 static std::string
108 get_notes (Transaction *trans)
109 {
110  auto notes{xaccTransGetNotes (trans)};
111  return (notes ? notes : "");
112 }
113 
114 // Void reason
115 static std::string
116 get_void_reason (Transaction *trans)
117 {
118  auto void_reason{xaccTransGetVoidReason (trans)};
119  return (void_reason ? void_reason : "");
120 }
121 
122 // Memo
123 static std::string
124 get_memo (Split *split)
125 {
126  auto memo{xaccSplitGetMemo (split)};
127  return (memo ? memo : "");
128 }
129 
130 // Full Category Path or Not
131 static std::string
132 get_category (Split *split, bool full)
133 {
134  auto other{xaccSplitGetOtherSplit(split)};
135  return other ? get_account_name (other, full) : _("-- Split Transaction --");
136 }
137 
138 // Action
139 static std::string
140 get_action (Split *split)
141 {
142  auto action{xaccSplitGetAction (split)};
143  return (action ? action : "");
144 }
145 
146 // Reconcile
147 static std::string
148 get_reconcile (Split *split)
149 {
150  auto recon{gnc_get_reconcile_str (xaccSplitGetReconcile (split))};
151  return (recon ? recon : "");
152 }
153 
154 // Transaction commodity
155 static std::string
156 get_commodity (Transaction *trans)
157 {
159 }
160 
161 // Amount with Symbol or not
162 static std::string
163 get_amount (Split *split, bool t_void, bool symbol)
164 {
165  auto amt_num{t_void ? xaccSplitVoidFormerAmount (split) : xaccSplitGetAmount (split)};
166  auto pinfo{gnc_split_amount_print_info (split, symbol)};
167  if (!symbol)
168  pinfo.use_separators = 0;
169  return xaccPrintAmount (amt_num, pinfo);
170 }
171 
172 // Value with Symbol or not
173 static std::string
174 get_value (Split *split, bool t_void, bool symbol)
175 {
176  auto trans{xaccSplitGetParent(split)};
177  auto tcurr{xaccTransGetCurrency (trans)};
178  auto pai{gnc_commodity_print_info (tcurr, symbol)};
179  if (!symbol)
180  pai.use_separators = 0;
181  auto amt_num{t_void ? xaccSplitVoidFormerValue (split): xaccSplitGetValue (split)};
182  return xaccPrintAmount (amt_num, pai);
183 }
184 
185 // Share Price / Conversion factor
186 static std::string
187 get_rate (Split *split, bool t_void)
188 {
189  auto curr{xaccAccountGetCommodity (xaccSplitGetAccount (split))};
190  auto amt_num{t_void ? gnc_numeric_zero() : xaccSplitGetSharePrice (split)};
191  return xaccPrintAmount (amt_num, gnc_default_price_print_info (curr));
192 }
193 
194 // Share Price / Conversion factor
195 static std::string
196 get_price (Split *split, bool t_void)
197 {
198  auto curr{xaccAccountGetCommodity (xaccSplitGetAccount (split))};
199  auto cf{t_void
204  : xaccSplitGetSharePrice (split)};
205  return xaccPrintAmount (cf, gnc_default_price_print_info (curr));
206 }
207 
208 /******************************************************************************/
209 
210 static StringVec
211 make_simple_trans_line (Transaction *trans, Split *split)
212 {
213  auto t_void{xaccTransGetVoidStatus (trans)};
214  return {
215  get_date (trans),
216  get_account_name (split, true),
217  get_number (trans),
218  get_description (trans),
219  get_category (split, true),
220  get_reconcile (split),
221  get_amount (split, t_void, true),
222  get_amount (split, t_void, false),
223  get_value (split, t_void, true),
224  get_value (split, t_void, false),
225  get_rate (split, t_void)
226  };
227 }
228 
229 static StringVec
230 make_complex_trans_line (Transaction *trans, Split *split)
231 {
232  auto t_void{xaccTransGetVoidStatus (trans)};
233  return {
234  get_date (trans),
235  get_guid (trans),
236  get_number (trans),
237  get_description (trans),
238  get_notes (trans),
239  get_commodity (trans),
240  get_void_reason (trans),
241  get_action (split),
242  get_memo (split),
243  get_account_name (split, true),
244  get_account_name (split, false),
245  get_amount (split, t_void, true),
246  get_amount (split, t_void, false),
247  get_value (split, t_void, true),
248  get_value (split, t_void, false),
249  get_reconcile (split),
250  get_reconcile_date (split),
251  get_price (split, t_void)
252  };
253 }
254 
255 using TransSet = std::unordered_set<Transaction*>;
256 
257 /*******************************************************
258  * account_splits
259  *
260  * gather the splits / transactions for an account and
261  * send them to a file
262  *******************************************************/
263 static void
264 export_query_splits (CsvExportInfo *info, bool is_trading_acct,
265  std::ofstream& ss, TransSet& trans_set)
266 {
267  g_return_if_fail (info);
268 
269  /* Run the query */
270  for (GList *splits = qof_query_run (info->query); !info->failed && splits;
271  splits = splits->next)
272  {
273  auto split{static_cast<Split*>(splits->data)};
274  auto trans{xaccSplitGetParent (split)};
275 
276  // Look for trans already exported in trans_set
277  if (!trans_set.emplace (trans).second)
278  continue;
279 
280  // Look for blank split
281  Account *split_acc = xaccSplitGetAccount (split);
282  if (!split_acc)
283  continue;
284 
285  // Only export trading splits when exporting a trading account
286  if (!is_trading_acct &&
287  (xaccAccountGetType (split_acc) == ACCT_TYPE_TRADING))
288  continue;
289 
290  if (info->simple_layout)
291  {
292  // Write line in simple layout, equivalent to a single line register view
293  auto line = make_simple_trans_line (trans, split);
294  info->failed = !gnc_csv_add_line (ss, line, info->use_quotes,
295  info->separator_str);
296  continue;
297  }
298 
299  // Write complex Transaction Line.
300  auto line = make_complex_trans_line (trans, split);
301  info->failed = !gnc_csv_add_line (ss, line, info->use_quotes,
302  info->separator_str);
303 
304  /* Loop through the list of splits for the Transaction */
305  for (auto node = xaccTransGetSplitList (trans); !info->failed && node;
306  node = node->next)
307  {
308  auto t_split{static_cast<Split*>(node->data)};
309 
310  // base split is already written on the trans_line
311  if (split == t_split)
312  continue;
313 
314  // Only export trading splits if exporting a trading account
315  Account *tsplit_acc = xaccSplitGetAccount (t_split);
316  if (!is_trading_acct &&
317  (xaccAccountGetType (tsplit_acc) == ACCT_TYPE_TRADING))
318  continue;
319 
320  // Write complex Split Line.
321  auto line = make_complex_trans_line (trans, t_split);
322  info->failed = !gnc_csv_add_line (ss, line, info->use_quotes,
323  info->separator_str);
324  }
325  }
326 }
327 
328 static void
329 account_splits (CsvExportInfo *info, Account *acc,
330  std::ofstream& ss, TransSet& trans_set)
331 {
332  g_return_if_fail (info && GNC_IS_ACCOUNT (acc));
333  // Setup the query for normal transaction export
334  auto p1 = g_slist_prepend (g_slist_prepend (nullptr, (gpointer)TRANS_DATE_POSTED), (gpointer)SPLIT_TRANS);
335  auto p2 = g_slist_prepend (nullptr, (gpointer)QUERY_DEFAULT_SORT);
336  info->query = qof_query_create_for (GNC_ID_SPLIT);
337  qof_query_set_book (info->query, gnc_get_current_book());
338  qof_query_set_sort_order (info->query, p1, p2, nullptr);
339  xaccQueryAddSingleAccountMatch (info->query, acc, QOF_QUERY_AND);
340  xaccQueryAddDateMatchTT (info->query, true, info->csvd.start_time, true, info->csvd.end_time, QOF_QUERY_AND);
341  export_query_splits (info, xaccAccountGetType (acc) == ACCT_TYPE_TRADING, ss, trans_set);
342  qof_query_destroy (info->query);
343 }
344 
345 /*******************************************************
346  * csv_transactions_export
347  *
348  * write a list of transactions to a text file
349  *******************************************************/
351 {
352  ENTER("");
353  DEBUG("File name is : %s", info->file_name);
354 
355  StringVec headers;
356  bool num_action = qof_book_use_split_action_for_num_field (gnc_get_current_book());
357 
358  /* Header string */
359  if (info->simple_layout)
360  {
361  /* Translators: The following symbols will build the header
362  line of exported CSV files: */
363  headers = {
364  _("Date"),
365  _("Account Name"),
366  (num_action ? _("Transaction Number") : _("Number")),
367  _("Description"),
368  _("Full Category Path"),
369  _("Reconcile"),
370  _("Amount With Sym"),
371  _("Amount Num."),
372  _("Value With Sym"),
373  _("Value Num."),
374  _("Rate/Price"),
375  };
376  }
377  else
378  headers = {
379  _("Date"),
380  _("Transaction ID"),
381  (num_action ? _("Transaction Number") : _("Number")),
382  _("Description"),
383  _("Notes"),
384  _("Commodity/Currency"),
385  _("Void Reason"),
386  (num_action ? _("Number/Action") : _("Action")),
387  _("Memo"),
388  _("Full Account Name"),
389  _("Account Name"),
390  _("Amount With Sym"),
391  _("Amount Num."),
392  _("Value With Sym"),
393  _("Value Num."),
394  _("Reconcile"),
395  _("Reconcile Date"),
396  _("Rate/Price"),
397  };
398 
399  /* Write header line */
400  auto ss{gnc_open_filestream(info->file_name)};
401  info->failed = !gnc_csv_add_line (ss, headers, info->use_quotes, info->separator_str);
402 
403  /* Go through list of accounts */
404  TransSet trans_set;
405 
406  switch (info->export_type)
407  {
408  case XML_EXPORT_TRANS:
409  for (auto ptr = info->csva.account_list; !ss.fail() && ptr; ptr = g_list_next(ptr))
410  account_splits (info, GNC_ACCOUNT(ptr->data), ss, trans_set);
411  break;
412  case XML_EXPORT_REGISTER:
413  export_query_splits (info, false, ss, trans_set);
414  break;
415  default:
416  PERR ("unknown export_type %d", info->export_type);
417  }
418 
419  info->failed = ss.fail();
420  LEAVE("");
421 }
422 
time64 xaccTransGetDate(const Transaction *trans)
Retrieve the posted date of the transaction.
utility functions for the GnuCash UI
void qof_query_set_sort_order(QofQuery *q, QofQueryParamList *params1, QofQueryParamList *params2, QofQueryParamList *params3)
When a query is run, the results are sorted before being returned.
Definition: qofquery.cpp:1249
GNCAccountType xaccAccountGetType(const Account *acc)
Returns the account&#39;s account type.
Definition: Account.cpp:3217
const char * xaccTransGetVoidReason(const Transaction *trans)
Returns the user supplied textual reason why a transaction was voided.
STRUCTS.
#define DEBUG(format, args...)
Print a debugging message.
Definition: qoflog.h:264
gboolean qof_book_use_split_action_for_num_field(const QofBook *book)
Returns TRUE if this book uses split action field as the &#39;Num&#39; field, FALSE if it uses transaction nu...
char xaccSplitGetReconcile(const Split *split)
Returns the value of the reconcile flag.
const char * xaccPrintAmount(gnc_numeric val, GNCPrintAmountInfo info)
Make a string representation of a gnc_numeric.
Transaction * xaccSplitGetParent(const Split *split)
Returns the parent transaction of the split.
const char * xaccTransGetNum(const Transaction *trans)
Gets the transaction Number (or ID) field; rather than use this function directly, see &#39;gnc_get_num_action&#39; and &#39;gnc_get_action_num&#39; in engine/engine-helpers.c & .h which takes a user-set book option for selecting the source for the num-cell (the transaction-number or the split-action field) in registers/reports into account automatically.
#define PERR(format, args...)
Log a serious error.
Definition: qoflog.h:244
#define ENTER(format, args...)
Print a function entry debugging message.
Definition: qoflog.h:272
Account used to record multiple commodity transactions.
Definition: Account.h:155
const char * xaccTransGetNotes(const Transaction *trans)
Gets the transaction Notes.
gnc_numeric xaccSplitVoidFormerAmount(const Split *split)
Returns the original pre-void amount of a split.
Definition: Split.cpp:2125
void qof_query_destroy(QofQuery *query)
Frees the resources associate with a Query object.
gnc_numeric xaccSplitVoidFormerValue(const Split *split)
Returns the original pre-void value of a split.
Definition: Split.cpp:2140
#define YREC
The Split has been reconciled.
Definition: Split.h:74
void qof_query_set_book(QofQuery *query, QofBook *book)
Set the book to be searched.
const char * xaccTransGetDescription(const Transaction *trans)
Gets the transaction Description.
#define MAX_DATE_LENGTH
The maximum length of a string created by the date printers.
Definition: gnc-date.h:108
gnc_numeric gnc_numeric_div(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Division.
gnc_numeric xaccSplitGetSharePrice(const Split *split)
Returns the price of the split, that is, the value divided by the amount.
Definition: Split.cpp:1932
const GncGUID * qof_entity_get_guid(gconstpointer ent)
CSV Export Transactions.
time64 xaccSplitGetDateReconciled(const Split *split)
Retrieve the date when the Split was reconciled.
Definition: Split.cpp:1825
gboolean xaccTransGetVoidStatus(const Transaction *trans)
Retrieve information on whether or not a transaction has been voided.
GList * qof_query_run(QofQuery *query)
Perform the query, return the results.
gnc_numeric xaccSplitGetValue(const Split *split)
Returns the value of this split in the transaction&#39;s commodity.
Definition: gmock-Split.cpp:84
Account * xaccSplitGetAccount(const Split *split)
Returns the account of this split, which was set through xaccAccountInsertSplit().
Definition: gmock-Split.cpp:53
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Get the account&#39;s commodity.
Definition: Account.cpp:3351
gnc_commodity * xaccTransGetCurrency(const Transaction *trans)
Returns the valuation commodity of this transaction.
Split * xaccSplitGetOtherSplit(const Split *split)
The xaccSplitGetOtherSplit() is a convenience routine that returns the other of a pair of splits...
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition: qoflog.h:282
const char * gnc_commodity_get_unique_name(const gnc_commodity *cm)
Retrieve the &#39;unique&#39; name for the specified commodity.
#define QUERY_DEFAULT_SORT
Default sort object type.
Definition: qofquery.h:105
Round to the nearest integer, rounding away from zero when there are two equidistant nearest integers...
Definition: gnc-numeric.h:165
void csv_transactions_export(CsvExportInfo *info)
The csv_transactions_export() will let the user export the transactions to a delimited file...
const char * xaccSplitGetMemo(const Split *split)
Returns the memo string.
Definition: gmock-Split.cpp:99
const char * xaccSplitGetAction(const Split *split)
Returns the action string.
File path resolution utility functions.
const char * xaccAccountGetName(const Account *acc)
Get the account&#39;s name.
Definition: Account.cpp:3239
#define GNC_DENOM_AUTO
Values that can be passed as the &#39;denom&#39; argument.
Definition: gnc-numeric.h:245
API for Transactions and Splits (journal entries)
#define GNC_HOW_DENOM_SIGFIGS(n)
Build a &#39;how&#39; value that will generate a denominator that will keep at least n significant figures in...
Definition: gnc-numeric.h:217
size_t qof_print_date_buff(char *buff, size_t buflen, time64 secs)
Convenience: calls through to qof_print_date_dmy_buff().
Definition: gnc-date.cpp:573
SplitList * xaccTransGetSplitList(const Transaction *trans)
The xaccTransGetSplitList() method returns a GList of the splits in a transaction.
Commodity handling public routines.
gnc_numeric xaccSplitGetAmount(const Split *split)
Returns the amount of the split in the account&#39;s commodity.
Definition: gmock-Split.cpp:69