GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
csv-transactions-export.c
Go to the documentation of this file.
1 /*******************************************************************\
2  * csv-transactions-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 [email protected] *
22 \********************************************************************/
27 #include "config.h"
28 
29 #include <gtk/gtk.h>
30 #include <glib/gi18n.h>
31 #include <glib/gstdio.h>
32 
33 #include "gnc-commodity.h"
34 #include "gnc-ui-util.h"
35 #include "Query.h"
36 #include "Transaction.h"
37 #include "engine-helpers.h"
38 #include "qofbookslots.h"
39 
41 
42 /* This static indicates the debugging module that this .o belongs to. */
43 static QofLogModule log_module = GNC_MOD_ASSISTANT;
44 
45 /* CSV spec requires CRLF line endings. Tweak the end-of-line string so this
46  * true for each platform */
47 #ifdef G_OS_WIN32
48 # define EOLSTR "\n"
49 #else
50 # define EOLSTR "\r\n"
51 #endif
52 
53 
54 /*******************************************************************/
55 
56 /*******************************************************
57  * write_line_to_file
58  *
59  * write a text string to a file pointer, return TRUE if
60  * successfull.
61  *******************************************************/
62 static
63 gboolean write_line_to_file (FILE *fh, char * line)
64 {
65  int len, written;
66  DEBUG("Account String: %s", line);
67 
68  /* Write account line */
69  len = strlen (line);
70  written = fwrite (line, 1, len, fh);
71 
72  if (written != len)
73  return FALSE;
74  else
75  return TRUE;
76 }
77 
78 
79 /*******************************************************
80  * csv_txn_test_field_string
81  *
82  * Test the field string for ," and new lines
83  *******************************************************/
84 static
85 gchar *csv_txn_test_field_string (CsvExportInfo *info, const gchar *string_in)
86 {
87  gboolean need_quote = FALSE;
88  gchar **parts;
89  gchar *string_parts;
90  gchar *string_out;
91 
92  /* Check for " and then "" them */
93  parts = g_strsplit (string_in, "\"", -1);
94  string_parts = g_strjoinv ("\"\"", parts);
95  g_strfreev (parts);
96 
97  /* Check for separator string and \n and " in field,
98  if so quote field if not allready quoted */
99  if (g_strrstr (string_parts, info->separator_str) != NULL)
100  need_quote = TRUE;
101  if (g_strrstr (string_parts, "\n") != NULL)
102  need_quote = TRUE;
103  if (g_strrstr (string_parts, "\"") != NULL)
104  need_quote = TRUE;
105 
106  if (!info->use_quotes && need_quote)
107  string_out = g_strconcat ("\"", string_parts, "\"", NULL);
108  else
109  string_out = g_strdup (string_parts);
110 
111  g_free (string_parts);
112  return string_out;
113 }
114 
115 
116 /*******************************************************
117  * account_splits
118  *
119  * gather the splits / transactions for an account and
120  * send them to a file
121  *******************************************************/
122 static
123 void account_splits (CsvExportInfo *info, Account *acc, FILE *fh )
124 {
125  Query *q;
126  GSList *p1, *p2;
127  GList *splits;
128  QofBook *book;
129 
130  gchar *end_sep;
131  gchar *mid_sep;
132 
133  q = qof_query_create_for (GNC_ID_SPLIT);
134  book = gnc_get_current_book();
135  qof_query_set_book (q, book);
136 
137  /* Set up separators */
138  if (info->use_quotes)
139  {
140  end_sep = "\"";
141  mid_sep = g_strconcat ("\"", info->separator_str, "\"", NULL);
142  }
143  else
144  {
145  end_sep = "";
146  mid_sep = g_strconcat (info->separator_str, NULL);
147  }
148 
149  /* Sort by transaction date */
150  p1 = g_slist_prepend (NULL, TRANS_DATE_POSTED);
151  p1 = g_slist_prepend (p1, SPLIT_TRANS);
152  p2 = g_slist_prepend (NULL, QUERY_DEFAULT_SORT);
153  qof_query_set_sort_order (q, p1, p2, NULL);
154 
155  xaccQueryAddSingleAccountMatch (q, acc, QOF_QUERY_AND);
156  xaccQueryAddDateMatchTT (q, TRUE, info->csvd.start_time, TRUE, info->csvd.end_time, QOF_QUERY_AND);
157 
158  /* Run the query */
159  for (splits = qof_query_run (q); splits; splits = splits->next)
160  {
161  Split *split;
162  Transaction *trans;
163  SplitList *s_list;
164  GList *node;
165  Split *t_split;
166  int nSplits;
167  int cnt;
168  gchar *part1;
169  gchar *part2;
170  gchar *date;
171  const gchar *currentSel;
172  const gchar *split_amount;
173  gchar *str_temp = NULL;
174  gchar *full_path = NULL;
175  Timespec ts = {0,0};
176  char type;
177  static char ss[2];
178 
179  split = splits->data;
180  trans = xaccSplitGetParent (split);
181  nSplits = xaccTransCountSplits (trans);
182  s_list = xaccTransGetSplitList (trans);
183  type = xaccTransGetTxnType (trans);
184 
185  // Look for trans already exported in trans_list
186  if (g_list_find (info->trans_list, trans) != NULL)
187  continue;
188 
189  /* Date */
190  date = qof_print_date (xaccTransGetDate (trans));
191  part1 = g_strconcat (end_sep, date, mid_sep, NULL);
192  g_free (date);
193  /* Transaction Type */
194  if (type == TXN_TYPE_NONE)
195  type = ' ';
196  ss[0] = type;
197  ss[1] = '\0';
198  part2 = g_strconcat (part1, ss, mid_sep, NULL);
199  g_free (part1);
200  /* Second Date */
201  if (type == TXN_TYPE_INVOICE)
202  {
203  xaccTransGetDateDueTS (trans, &ts);
204  currentSel = gnc_print_date (ts);
205  part1 = g_strconcat (part2, currentSel, mid_sep, NULL);
206  g_free (part2);
207  }
208  else
209  {
210  part1 = g_strconcat (part2, mid_sep, NULL);
211  g_free (part2);
212  }
213  /* Name */
214  currentSel = xaccAccountGetName (acc);
215  str_temp = csv_txn_test_field_string (info, currentSel);
216  part2 = g_strconcat (part1, str_temp, mid_sep, NULL);
217  g_free (str_temp);
218  g_free (part1);
219  /* Number */
220  currentSel = xaccTransGetNum (trans) ? xaccTransGetNum (trans) : "" ;
221  str_temp = csv_txn_test_field_string (info, currentSel);
222  part1 = g_strconcat (part2, str_temp, mid_sep, NULL);
223  g_free (str_temp);
224  g_free (part2);
225  /* Description */
226  currentSel = xaccTransGetDescription (trans) ? xaccTransGetDescription (trans) : "" ;
227  str_temp = csv_txn_test_field_string (info, currentSel);
228  part2 = g_strconcat (part1, str_temp, mid_sep, NULL);
229  g_free (str_temp);
230  g_free (part1);
231  /* Notes */
232  currentSel = xaccTransGetNotes (trans) ? xaccTransGetNotes (trans) : "" ;
233  str_temp = csv_txn_test_field_string (info, currentSel);
234  part1 = g_strconcat (part2, str_temp, mid_sep, NULL);
235  g_free (str_temp);
236  g_free (part2);
237  /* Memo */
238  currentSel = xaccSplitGetMemo (split) ? xaccSplitGetMemo (split) : "" ;
239  str_temp = csv_txn_test_field_string (info, currentSel);
240  part2 = g_strconcat (part1, str_temp, mid_sep, NULL);
241  g_free (str_temp);
242  g_free (part1);
243  /* Full Category Path */
244  full_path = xaccSplitGetCorrAccountFullName (split);
245  str_temp = csv_txn_test_field_string (info, full_path);
246  part1 = g_strconcat (part2, str_temp, mid_sep, NULL);
247  g_free (full_path);
248  g_free (str_temp);
249  g_free (part2);
250  part2 = g_strconcat (part1, NULL);
251  g_free (part1);
252  /* Category */
253  currentSel = xaccSplitGetCorrAccountName (split);
254  str_temp = csv_txn_test_field_string (info, currentSel);
255  part1 = g_strconcat (part2, str_temp, mid_sep, "T", mid_sep, "", mid_sep, NULL);
256  g_free (str_temp);
257  g_free (part2);
258 
259  part2 = g_strconcat (part1, NULL);
260  g_free (part1);
261 
262  /* Reconcile */
263  currentSel = gnc_get_reconcile_str (xaccSplitGetReconcile (split));
264  part1 = g_strconcat (part2, currentSel, mid_sep, NULL);
265  g_free (part2);
266 
267  /* To with Symbol */
268  part2 = g_strconcat (part1, "", mid_sep, NULL);
269  g_free (part1);
270 
271  /* From with Symbol */
272  part1 = g_strconcat (part2, "", mid_sep, NULL);
273  g_free (part2);
274 
275  /* Commodity Mnemonic */
276  currentSel = gnc_commodity_get_mnemonic (xaccTransGetCurrency (trans));
277  str_temp = csv_txn_test_field_string (info, currentSel);
278  part2 = g_strconcat (part1, str_temp, mid_sep, NULL);
279  g_free (str_temp);
280  g_free (part1);
281 
282  /* Commodity Namespace */
283  currentSel = gnc_commodity_get_namespace (xaccTransGetCurrency (trans));
284  str_temp = csv_txn_test_field_string (info, currentSel);
285  part1 = g_strconcat (part2, str_temp, mid_sep, NULL);
286  g_free (str_temp);
287  g_free (part2);
288 
289  /* To Number Only */
290  part2 = g_strconcat (part1, "", mid_sep, NULL);
291  g_free (part1);
292 
293  /* From Number Only */
294  part1 = g_strconcat (part2, "", mid_sep, "", mid_sep, "", end_sep, EOLSTR, NULL);
295  g_free (part2);
296 
297  /* Write to file */
298  if (!write_line_to_file (fh, part1))
299  {
300  info->failed = TRUE;
301  break;
302  }
303  g_free (part1);
304 
305  /* Loop through the list of splits for the Transcation */
306  node = s_list;
307  cnt = 0;
308  while ((cnt < nSplits) && (info->failed == FALSE))
309  {
310  gchar *fullname = NULL;
311  const gchar *str_rec_date;
312  gboolean t_void = xaccTransGetVoidStatus (trans);
313  t_split = node->data;
314 
315 
316  if (xaccSplitGetReconcile (t_split) == YREC)
317  {
318  xaccSplitGetDateReconciledTS (t_split, &ts);
319  str_rec_date = gnc_print_date (ts);
320  }
321  else
322  str_rec_date = "";
323 
324  /* Start of line */
325  if (t_void)
326  {
327  currentSel = xaccTransGetVoidReason (trans) ? xaccTransGetVoidReason (trans) : "" ;
328  str_temp = csv_txn_test_field_string (info, currentSel);
329  part1 = g_strconcat (end_sep, mid_sep, mid_sep, str_rec_date, mid_sep, mid_sep, mid_sep, mid_sep, str_temp, mid_sep, NULL);
330  g_free (str_temp);
331  }
332  else
333  part1 = g_strconcat (end_sep, mid_sep, mid_sep, str_rec_date, mid_sep, mid_sep, mid_sep, mid_sep, mid_sep, NULL);
334 
335  /* Memo */
336  currentSel = xaccSplitGetMemo (t_split) ? xaccSplitGetMemo (t_split) : "" ;
337  str_temp = csv_txn_test_field_string (info, currentSel);
338  part2 = g_strconcat (part1, str_temp, mid_sep, NULL);
339  g_free (str_temp);
340  g_free (part1);
341 
342  /* Full Account */
343  fullname = gnc_account_get_full_name (xaccSplitGetAccount (t_split));
344  str_temp = csv_txn_test_field_string (info, fullname);
345  part1 = g_strconcat (part2, str_temp, mid_sep, NULL);
346  g_free (str_temp);
347  g_free (fullname);
348  g_free (part2);
349 
350  part2 = g_strconcat (part1, NULL);
351  g_free (part1);
352 
353  /* Account */
354  currentSel = xaccAccountGetName (xaccSplitGetAccount (t_split));
355  str_temp = csv_txn_test_field_string (info, currentSel);
356  part1 = g_strconcat (part2, str_temp, mid_sep, "S", mid_sep, NULL);
357  g_free (str_temp);
358  g_free (part2);
359 
360  /* Action */
361  currentSel = xaccSplitGetAction (t_split);
362  str_temp = csv_txn_test_field_string (info, currentSel);
363  part2 = g_strconcat (part1, str_temp, mid_sep, NULL);
364  g_free (str_temp);
365  g_free (part1);
366 
367  /* Reconcile */
368  currentSel = gnc_get_reconcile_str (xaccSplitGetReconcile (t_split));
369  part1 = g_strconcat (part2, currentSel, mid_sep, NULL);
370  g_free (part2);
371 
372  /* From / To with Symbol */
373  split_amount = xaccPrintAmount (xaccSplitGetAmount (t_split), gnc_split_amount_print_info (t_split, TRUE));
374  str_temp = csv_txn_test_field_string (info, split_amount);
375  if (xaccSplitGetAccount(t_split) == acc)
376  part2 = g_strconcat (part1, str_temp, mid_sep, mid_sep, NULL);
377  else
378  part2 = g_strconcat (part1, mid_sep, str_temp, mid_sep, NULL);
379  g_free (str_temp);
380  g_free (part1);
381 
382  /* Commodity Mnemonic */
384  str_temp = csv_txn_test_field_string (info, currentSel);
385  part1 = g_strconcat (part2, str_temp, mid_sep, NULL);
386  g_free (str_temp);
387  g_free (part2);
388 
389  /* Commodity Namespace */
391  str_temp = csv_txn_test_field_string (info, currentSel);
392  part2 = g_strconcat (part1, str_temp, mid_sep, NULL);
393  g_free (str_temp);
394  g_free (part1);
395 
396  /* From / To Numbers only */
397  if (t_void)
398  split_amount = xaccPrintAmount (xaccSplitVoidFormerAmount (t_split), gnc_split_amount_print_info (t_split, FALSE));
399  else
400  split_amount = xaccPrintAmount (xaccSplitGetAmount (t_split), gnc_split_amount_print_info (t_split, FALSE));
401  str_temp = csv_txn_test_field_string (info, split_amount);
402  if (xaccSplitGetAccount (t_split) == acc)
403  part1 = g_strconcat (part2, str_temp, mid_sep, mid_sep, NULL);
404  else
405  part1 = g_strconcat (part2, mid_sep, str_temp, mid_sep, NULL);
406  g_free (str_temp);
407  g_free (part2);
408 
409  /* From / To - Share Price / Conversion factor */
410  if (t_void)
411  {
414  split_amount = xaccPrintAmount (cf, gnc_split_amount_print_info (t_split, FALSE));
415  }
416  else
417  split_amount = xaccPrintAmount (xaccSplitGetSharePrice (t_split), gnc_split_amount_print_info (t_split, FALSE));
418  str_temp = csv_txn_test_field_string (info, split_amount);
419  if (xaccSplitGetAccount (t_split) == acc)
420  part2 = g_strconcat (part1, str_temp, mid_sep, end_sep, EOLSTR, NULL);
421  else
422  part2 = g_strconcat (part1, mid_sep, str_temp, end_sep, EOLSTR, NULL);
423  g_free (str_temp);
424  g_free (part1);
425 
426  if (!write_line_to_file (fh, part2))
427  info->failed = TRUE;
428 
429  g_free (part2);
430  cnt++;
431  node = node->next;
432  }
433 
434  info->trans_list = g_list_prepend (info->trans_list, trans); // add trans to trans_list
435 
436  }
437  g_free (mid_sep);
438  qof_query_destroy (q);
439  g_list_free (splits);
440 }
441 
442 
443 /*******************************************************
444  * csv_transactions_export
445  *
446  * write a list of transactions to a text file
447  *******************************************************/
449 {
450  FILE *fh;
451  Account *acc;
452  GList *ptr;
453  gboolean num_action = qof_book_use_split_action_for_num_field(gnc_get_current_book());
454 
455  ENTER("");
456  DEBUG("File name is : %s", info->file_name);
457 
458  info->failed = FALSE;
459 
460  /* Open File for writing */
461  fh = g_fopen (info->file_name, "w" );
462  if (fh != NULL)
463  {
464  gchar *header;
465  gchar *end_sep;
466  gchar *mid_sep;
467  int i;
468 
469  /* Set up separators */
470  if (info->use_quotes)
471  {
472  end_sep = "\"";
473  mid_sep = g_strconcat ("\"", info->separator_str, "\"", NULL);
474  }
475  else
476  {
477  end_sep = "";
478  mid_sep = g_strconcat (info->separator_str, NULL);
479  }
480 
481  /* Header string */
482  header = g_strconcat (end_sep, _("Date"), mid_sep, _("Transaction Type"), mid_sep, _("Second Date"),
483  mid_sep, _("Account Name"), mid_sep, (num_action ? _("Transaction Number") : _("Number")),
484  mid_sep, _("Description"), mid_sep, _("Notes"), mid_sep, _("Memo"),
485  mid_sep, _("Full Category Path"), mid_sep, _("Category"), mid_sep, _("Row Type"),
486  mid_sep, (num_action ? _("Number/Action") : _("Action")),
487  mid_sep, _("Reconcile"), mid_sep, _("To With Sym"), mid_sep, _("From With Sym"),
488  mid_sep, _("Commodity Mnemonic"), mid_sep, _("Commodity Namespace"),
489  mid_sep, _("To Num."), mid_sep, _("From Num."), mid_sep, _("To Rate/Price"),
490  mid_sep, _("From Rate/Price"),
491  end_sep, EOLSTR, NULL);
492  DEBUG("Header String: %s", header);
493 
494  /* Write header line */
495  if (!write_line_to_file (fh, header))
496  {
497  info->failed = TRUE;
498  g_free (mid_sep);
499  g_free (header);
500  return;
501  }
502  g_free (mid_sep);
503  g_free (header);
504 
505  /* Go through list of accounts */
506  for (ptr = info->csva.account_list, i = 0; ptr; ptr = g_list_next(ptr), i++)
507  {
508  acc = ptr->data;
509  DEBUG("Account being processed is : %s", xaccAccountGetName (acc));
510  account_splits (info, acc, fh);
511  }
512  g_list_free (info->trans_list); // free trans_list
513  }
514  else
515  info->failed = TRUE;
516  if (fh)
517  fclose (fh);
518  LEAVE("");
519 }
520 
char xaccTransGetTxnType(const Transaction *trans)
Definition: Transaction.c:2302
void xaccTransGetDateDueTS(const Transaction *trans, Timespec *ts)
Definition: Transaction.c:2280
time64 xaccTransGetDate(const Transaction *trans)
Definition: Transaction.c:2215
const char * gnc_print_date(Timespec ts)
const char * gnc_commodity_get_mnemonic(const gnc_commodity *cm)
utility functions for the GnuCash UI
#define TXN_TYPE_INVOICE
Definition: Transaction.h:120
void qof_query_set_sort_order(QofQuery *q, QofQueryParamList *primary_sort_params, QofQueryParamList *secondary_sort_params, QofQueryParamList *tertiary_sort_params)
const char * xaccTransGetVoidReason(const Transaction *trans)
Definition: Transaction.c:2533
#define DEBUG(format, args...)
Definition: qoflog.h:255
gboolean qof_book_use_split_action_for_num_field(const QofBook *book)
char xaccSplitGetReconcile(const Split *split)
Definition: Split.c:1980
Use a 64-bit unsigned int timespec.
Definition: gnc-date.h:299
void csv_transactions_export(CsvExportInfo *info)
Transaction * xaccSplitGetParent(const Split *split)
Definition: Split.c:1903
const char * gnc_commodity_get_namespace(const gnc_commodity *cm)
const char * xaccTransGetNum(const Transaction *trans)
Definition: Transaction.c:2178
#define ENTER(format, args...)
Definition: qoflog.h:261
void xaccSplitGetDateReconciledTS(const Split *split, Timespec *ts)
Definition: Split.c:1877
const char * xaccTransGetNotes(const Transaction *trans)
Definition: Transaction.c:2197
char * qof_print_date(time64 secs)
int xaccTransCountSplits(const Transaction *trans)
Definition: Transaction.c:2170
#define TXN_TYPE_NONE
Definition: Transaction.h:119
gnc_numeric xaccSplitVoidFormerAmount(const Split *split)
Definition: Split.c:2144
GList SplitList
Definition: gnc-engine.h:203
gchar * gnc_account_get_full_name(const Account *account)
Definition: Account.c:3038
void qof_query_destroy(QofQuery *q)
gnc_numeric xaccSplitVoidFormerValue(const Split *split)
Definition: Split.c:2151
#define YREC
Definition: Split.h:68
void qof_query_set_book(QofQuery *q, QofBook *book)
char * xaccSplitGetCorrAccountFullName(const Split *sa)
Definition: Split.c:1664
const char * xaccTransGetDescription(const Transaction *trans)
Definition: Transaction.c:2184
gnc_numeric gnc_numeric_div(gnc_numeric x, gnc_numeric y, gint64 denom, gint how)
gnc_numeric xaccSplitGetSharePrice(const Split *split)
Definition: Split.c:1999
CSV Export Transactions.
const char * xaccSplitGetCorrAccountName(const Split *sa)
Definition: Split.c:1647
gboolean xaccTransGetVoidStatus(const Transaction *trans)
Definition: Transaction.c:2526
GList * qof_query_run(QofQuery *query)
Definition: SplitP.h:71
Account * xaccSplitGetAccount(const Split *s)
Definition: Split.c:968
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Definition: Account.c:3148
gnc_commodity * xaccTransGetCurrency(const Transaction *trans)
Definition: Transaction.c:1348
#define LEAVE(format, args...)
Definition: qoflog.h:271
#define QUERY_DEFAULT_SORT
Definition: qofquery.h:106
const char * xaccSplitGetMemo(const Split *split)
Definition: Split.c:1968
const char * xaccSplitGetAction(const Split *split)
Definition: Split.c:1974
const char * xaccAccountGetName(const Account *acc)
Definition: Account.c:3031
#define GNC_DENOM_AUTO
Definition: gnc-numeric.h:246
API for Transactions and Splits (journal entries)
#define GNC_HOW_DENOM_SIGFIGS(n)
Definition: gnc-numeric.h:218
SplitList * xaccTransGetSplitList(const Transaction *trans)
Definition: Transaction.c:2164
Commodity handling public routines.
const gchar * QofLogModule
Definition: qofid.h:89
gnc_numeric xaccSplitGetAmount(const Split *split)
Definition: Split.c:1987