GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
gnc-log-replay.c
1 /********************************************************************\
2  * This program is free software; you can redistribute it and/or *
3  * modify it under the terms of the GNU General Public License as *
4  * published by the Free Software Foundation; either version 2 of *
5  * the License, or (at your option) any later version. *
6  * *
7  * This program is distributed in the hope that it will be useful, *
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
10  * GNU General Public License for more details. *
11  * *
12  * You should have received a copy of the GNU General Public License*
13  * along with this program; if not, contact: *
14  * *
15  * Free Software Foundation Voice: +1-617-542-5942 *
16  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
17  * Boston, MA 02110-1301, USA [email protected] *
18 \********************************************************************/
26 #include "config.h"
27 
28 #include <gtk/gtk.h>
29 #include <glib/gi18n.h>
30 #include <glib/gstdio.h>
31 #include <string.h>
32 #include <sys/time.h>
33 #include <errno.h>
34 
35 #include "Account.h"
36 #include "Transaction.h"
37 #include "TransactionP.h"
38 #include "TransLog.h"
39 #include "Scrub.h"
40 #include "gnc-log-replay.h"
41 #include "gnc-file.h"
42 #include "qof.h"
43 #include "gnc-ui-util.h"
44 #include "gnc-gui-query.h"
45 
46 #define GNC_PREFS_GROUP "dialogs.log-replay"
47 
48 /* EFFECTIVE FRIEND FUNCTION */
49 void qof_instance_set_guid (gpointer inst, const GncGUID *guid);
50 
51 /* NW: If you want a new log_module, just define
52 a unique string either in gnc-engine.h or
53 locally.*/
54 /*static QofLogModule log_module = GNC_MOD_IMPORT;*/
55 static QofLogModule log_module = GNC_MOD_TEST;
56 
57 /* fprintf (trans_log, "mod guid time_now " \
58  "date_entered date_posted " \
59  "acc_guid acc_name num description " \
60  "memo action reconciled " \
61  "amount value date_reconciled\n");
62  "%c\t%s/%s\t%s\t%s\t%s\t%s\t%s\t%s\t"
63  "%s\t%s\t%s\t%c\t%lld/%lld\t%lld/%lld\t%s\n",
64 */
65 #define STRING_FIELD_SIZE 256
66 typedef struct _split_record
67 {
68  enum _enum_action {LOG_BEGIN_EDIT, LOG_ROLLBACK, LOG_COMMIT, LOG_DELETE} log_action;
69  int log_action_present;
70  GncGUID trans_guid;
71  int trans_guid_present;
72  GncGUID split_guid;
73  int split_guid_present;
74  Timespec log_date;
75  int log_date_present;
76  Timespec date_entered;
77  int date_entered_present;
78  Timespec date_posted;
79  int date_posted_present;
80  GncGUID acc_guid;
81  int acc_guid_present;
82  char acc_name[STRING_FIELD_SIZE];
83  int acc_name_present;
84  char trans_num[STRING_FIELD_SIZE];
85  int trans_num_present;
86  char trans_descr[STRING_FIELD_SIZE];
87  int trans_descr_present;
88  char trans_notes[STRING_FIELD_SIZE];
89  int trans_notes_present;
90  char split_memo[STRING_FIELD_SIZE];
91  int split_memo_present;
92  char split_action[STRING_FIELD_SIZE];
93  int split_action_present;
94  char split_reconcile;
95  int split_reconcile_present;
96  gnc_numeric amount;
97  int amount_present;
98  gnc_numeric value;
99  int value_present;
100  Timespec date_reconciled;
101  int date_reconciled_present;
102 } split_record;
103 /********************************************************************\
104  * gnc_file_log_replay_import
105  * Entry point
106 \********************************************************************/
107 
108 static char *olds;
109 /* This version of strtok will only match SINGLE occurence of delim,
110  returning a 0 length valid string between two consecutive ocurence of delim.
111  It will also return a 0 length string instead of NULL when it reaches the end of s
112 */
113 static char * my_strtok (s, delim)
114 char *s;
115 const char *delim;
116 {
117  char *token;
118  /*DEBUG("strtok(): Start...");*/
119  if (s == NULL)
120  s = olds;
121 
122  /* Scan leading delimiters. */
123  /*s += strspn (s, delim);*/ /*Don't do it, or we will loose count.*/
124  if (*s == '\0')
125  {
126  olds = s;
127  return s;
128  }
129 
130  /* Find the end of the token. */
131  token = s;
132  s = strpbrk (token, delim);
133  if (s == NULL)
134  {
135  /* This token finishes the string. */
136  olds = strchr (token, '\0');
137  }
138  else
139  {
140  /* Terminate the token and make OLDS point past it. */
141  *s = '\0';
142  olds = s + 1;
143  }
144  return token;
145 }
146 
147 static split_record interpret_split_record( char *record_line)
148 {
149  char * tok_ptr;
150  split_record record;
151  memset(&record, 0, sizeof(record));
152  DEBUG("interpret_split_record(): Start...");
153  if (strlen(tok_ptr = my_strtok(record_line, "\t")) != 0)
154  {
155  switch (tok_ptr[0])
156  {
157  case 'B':
158  record.log_action = LOG_BEGIN_EDIT;
159  break;
160  case 'D':
161  record.log_action = LOG_DELETE;
162  break;
163  case 'C':
164  record.log_action = LOG_COMMIT;
165  break;
166  case 'R':
167  record.log_action = LOG_ROLLBACK;
168  break;
169  }
170  record.log_action_present = TRUE;
171  }
172  if (strlen(tok_ptr = my_strtok(NULL, "\t")) != 0)
173  {
174  string_to_guid(tok_ptr, &(record.trans_guid));
175  record.trans_guid_present = TRUE;
176  }
177  if (strlen(tok_ptr = my_strtok(NULL, "\t")) != 0)
178  {
179  string_to_guid(tok_ptr, &(record.split_guid));
180  record.split_guid_present = TRUE;
181  }
182  if (strlen(tok_ptr = my_strtok(NULL, "\t")) != 0)
183  {
184  record.log_date = gnc_iso8601_to_timespec_gmt(tok_ptr);
185  record.log_date_present = TRUE;
186  }
187  if (strlen(tok_ptr = my_strtok(NULL, "\t")) != 0)
188  {
189  record.date_entered = gnc_iso8601_to_timespec_gmt(tok_ptr);
190  record.date_entered_present = TRUE;
191  }
192  if (strlen(tok_ptr = my_strtok(NULL, "\t")) != 0)
193  {
194  record.date_posted = gnc_iso8601_to_timespec_gmt(tok_ptr);
195  record.date_posted_present = TRUE;
196  }
197  if (strlen(tok_ptr = my_strtok(NULL, "\t")) != 0)
198  {
199  string_to_guid(tok_ptr, &(record.acc_guid));
200  record.acc_guid_present = TRUE;
201  }
202  if (strlen(tok_ptr = my_strtok(NULL, "\t")) != 0)
203  {
204  strncpy(record.acc_name, tok_ptr, STRING_FIELD_SIZE - 1);
205  record.acc_name_present = TRUE;
206  }
207  if (strlen(tok_ptr = my_strtok(NULL, "\t")) != 0)
208  {
209  strncpy(record.trans_num, tok_ptr, STRING_FIELD_SIZE - 1);
210  record.trans_num_present = TRUE;
211  }
212  if (strlen(tok_ptr = my_strtok(NULL, "\t")) != 0)
213  {
214  strncpy(record.trans_descr, tok_ptr, STRING_FIELD_SIZE - 1);
215  record.trans_descr_present = TRUE;
216  }
217  if (strlen(tok_ptr = my_strtok(NULL, "\t")) != 0)
218  {
219  strncpy(record.trans_notes, tok_ptr, STRING_FIELD_SIZE - 1);
220  record.trans_notes_present = TRUE;
221  }
222  if (strlen(tok_ptr = my_strtok(NULL, "\t")) != 0)
223  {
224  strncpy(record.split_memo, tok_ptr, STRING_FIELD_SIZE - 1);
225  record.split_memo_present = TRUE;
226  }
227  if (strlen(tok_ptr = my_strtok(NULL, "\t")) != 0)
228  {
229  strncpy(record.split_action, tok_ptr, STRING_FIELD_SIZE - 1);
230  record.split_action_present = TRUE;
231  }
232  if (strlen(tok_ptr = my_strtok(NULL, "\t")) != 0)
233  {
234  record.split_reconcile = tok_ptr[0];
235  record.split_reconcile_present = TRUE;
236  }
237  if (strlen(tok_ptr = my_strtok(NULL, "\t")) != 0)
238  {
239  string_to_gnc_numeric(tok_ptr, &(record.amount));
240  record.amount_present = TRUE;
241  }
242  if (strlen(tok_ptr = my_strtok(NULL, "\t")) != 0)
243  {
244  string_to_gnc_numeric(tok_ptr, &(record.value));
245  record.value_present = TRUE;
246  }
247  if (strlen(tok_ptr = my_strtok(NULL, "\t")) != 0)
248  {
249  record.date_reconciled = gnc_iso8601_to_timespec_gmt(tok_ptr);
250  record.date_reconciled_present = TRUE;
251  }
252 
253  if (strlen(tok_ptr = my_strtok(NULL, "\t")) != 0)
254  {
255  PERR("interpret_split_record(): Expected number of fields exceeded!");
256  }
257  DEBUG("interpret_split_record(): End");
258  return record;
259 }
260 
261 static void dump_split_record(split_record record)
262 {
263  char * string_ptr = NULL;
264  char string_buf[256];
265 
266  DEBUG("dump_split_record(): Start...");
267  if (record.log_action_present)
268  {
269  switch (record.log_action)
270  {
271  case LOG_BEGIN_EDIT:
272  DEBUG("Log action: LOG_BEGIN_EDIT");
273  break;
274  case LOG_DELETE:
275  DEBUG("Log action: LOG_DELETE");
276  break;
277  case LOG_COMMIT:
278  DEBUG("Log action: LOG_COMMIT");
279  break;
280  case LOG_ROLLBACK:
281  DEBUG("Log action: LOG_ROLLBACK");
282  break;
283  }
284  }
285  if (record.trans_guid_present)
286  {
287  guid_to_string_buff(&record.trans_guid, string_buf);
288  DEBUG("Transaction GncGUID: %s", string_buf);
289  }
290  if (record.split_guid_present)
291  {
292  guid_to_string_buff(&record.split_guid, string_buf);
293  DEBUG("Split GncGUID: %s", string_buf);
294  }
295  if (record.log_date_present)
296  {
297  gnc_timespec_to_iso8601_buff (record.log_date, string_buf);
298  DEBUG("Log entry date: %s", string_buf);
299  }
300  if (record.date_entered_present)
301  {
302  gnc_timespec_to_iso8601_buff (record.date_entered, string_buf);
303  DEBUG("Date entered: %s", string_buf);
304  }
305  if (record.date_posted_present)
306  {
307  gnc_timespec_to_iso8601_buff (record.date_posted, string_buf);
308  DEBUG("Date posted: %s", string_buf);
309  }
310  if (record.acc_guid_present)
311  {
312  guid_to_string_buff(&record.trans_guid, string_buf);
313  DEBUG("Account GncGUID: %s", string_buf);
314  }
315  if (record.acc_name_present)
316  {
317  DEBUG("Account name: %s", record.acc_name);
318  }
319  if (record.trans_num_present)
320  {
321  DEBUG("Transaction number: %s", record.trans_num);
322  }
323  if (record.trans_descr_present)
324  {
325  DEBUG("Transaction description: %s", record.trans_descr);
326  }
327  if (record.trans_notes_present)
328  {
329  DEBUG("Transaction notes: %s", record.trans_notes);
330  }
331  if (record.split_memo_present)
332  {
333  DEBUG("Split memo: %s", record.split_memo);
334  }
335  if (record.split_action_present)
336  {
337  DEBUG("Split action: %s", record.split_action);
338  }
339  if (record.split_reconcile_present)
340  {
341  DEBUG("Split reconcile: %c", record.split_reconcile);
342  }
343  if (record.amount_present)
344  {
345  string_ptr = gnc_numeric_to_string(record.amount);
346  DEBUG("Record amount: %s", string_ptr);
347  g_free(string_ptr);
348  }
349  if (record.value_present)
350  {
351  string_ptr = gnc_numeric_to_string(record.value);
352  DEBUG("Record value: %s", string_ptr);
353  g_free(string_ptr);
354  }
355  if (record.date_reconciled_present)
356  {
357  gnc_timespec_to_iso8601_buff (record.date_reconciled, string_buf);
358  DEBUG("Reconciled date: %s", string_buf);
359  }
360 }
361 
362 /* File pointer must already be at the begining of a record */
363 static void process_trans_record( FILE *log_file)
364 {
365  char read_buf[2048];
366  char *read_retval;
367  char * trans_ro = NULL;
368  const char * record_end_str = "===== END";
369  int first_record = TRUE;
370  int record_ended = FALSE;
371  int split_num = 0;
372  split_record record;
373  Transaction * trans = NULL;
374  Split * split = NULL;
375  Account * acct = NULL;
376  QofBook * book = gnc_get_current_book();
377 
378  DEBUG("process_trans_record(): Begin...\n");
379 
380  while ( record_ended == FALSE)
381  {
382  read_retval = fgets(read_buf, sizeof(read_buf), log_file);
383  if (read_retval != NULL && strncmp(record_end_str, read_buf, strlen(record_end_str)) != 0) /* If we are not at the end of the record */
384  {
385  split_num++;
386  /*DEBUG("process_trans_record(): Line read: %s%s",read_buf ,"\n");*/
387  record = interpret_split_record( read_buf);
388  dump_split_record( record);
389  if (record.log_action_present)
390  {
391  switch (record.log_action)
392  {
393  case LOG_BEGIN_EDIT:
394  DEBUG("process_trans_record():Ignoring log action: LOG_BEGIN_EDIT"); /*Do nothing, there is no point*/
395  break;
396  case LOG_ROLLBACK:
397  DEBUG("process_trans_record():Ignoring log action: LOG_ROLLBACK");/*Do nothing, since we didn't do the begin_edit either*/
398  break;
399  case LOG_DELETE:
400  DEBUG("process_trans_record(): Playing back LOG_DELETE");
401  if ((trans = xaccTransLookup (&(record.trans_guid), book)) != NULL
402  && first_record == TRUE)
403  {
404  first_record = FALSE;
405  if (xaccTransGetReadOnly(trans))
406  {
407  PWARN("Destroying a read only transaction.");
408  xaccTransClearReadOnly(trans);
409  }
410  xaccTransBeginEdit(trans);
411  xaccTransDestroy(trans);
412  }
413  else if (first_record == TRUE)
414  {
415  PERR("The transaction to delete was not found!");
416  }
417  else
418  xaccTransDestroy(trans);
419  break;
420  case LOG_COMMIT:
421  DEBUG("process_trans_record(): Playing back LOG_COMMIT");
422  if (record.trans_guid_present == TRUE
423  && first_record == TRUE)
424  {
425  trans = xaccTransLookupDirect (record.trans_guid, book);
426  if (trans != NULL)
427  {
428  DEBUG("process_trans_record(): Transaction to be edited was found");
429  xaccTransBeginEdit(trans);
430  trans_ro = g_strdup(xaccTransGetReadOnly(trans));
431  if (trans_ro)
432  {
433  PWARN("Replaying a read only transaction.");
434  xaccTransClearReadOnly(trans);
435  }
436  }
437  else
438  {
439  DEBUG("process_trans_record(): Creating a new transaction");
440  trans = xaccMallocTransaction (book);
441  xaccTransBeginEdit(trans);
442  }
443 
444  qof_instance_set_guid (QOF_INSTANCE (trans),
445  &(record.trans_guid));
446  /*Fill the transaction info*/
447  if (record.date_entered_present)
448  {
449  xaccTransSetDateEnteredTS(trans, &(record.date_entered));
450  }
451  if (record.date_posted_present)
452  {
453  xaccTransSetDatePostedTS(trans, &(record.date_posted));
454  }
455  if (record.trans_num_present)
456  {
457  xaccTransSetNum(trans, record.trans_num);
458  }
459  if (record.trans_descr_present)
460  {
461  xaccTransSetDescription(trans, record.trans_descr);
462  }
463  if (record.trans_notes_present)
464  {
465  xaccTransSetNotes(trans, record.trans_notes);
466  }
467  }
468  if (record.split_guid_present == TRUE) /*Fill the split info*/
469  {
470  gboolean is_new_split;
471 
472  split = xaccSplitLookupDirect (record.split_guid, book);
473  if (split != NULL)
474  {
475  DEBUG("process_trans_record(): Split to be edited was found");
476  is_new_split = FALSE;
477  }
478  else
479  {
480  DEBUG("process_trans_record(): Creating a new split");
481  split = xaccMallocSplit(book);
482  is_new_split = TRUE;
483  }
484  xaccSplitSetGUID (split, &(record.split_guid));
485  if (record.acc_guid_present)
486  {
487  acct = xaccAccountLookupDirect(record.acc_guid, book);
488  xaccAccountInsertSplit(acct, split);
489 
490  // No currency in the txn yet? Set one now.
491  if (!xaccTransGetCurrency(trans))
493  }
494  if (is_new_split)
495  xaccTransAppendSplit(trans, split);
496 
497  if (record.split_memo_present)
498  {
499  xaccSplitSetMemo(split, record.split_memo);
500  }
501  if (record.split_action_present)
502  {
503  xaccSplitSetAction(split, record.split_action);
504  }
505  if (record.date_reconciled_present)
506  {
507  xaccSplitSetDateReconciledTS (split, &(record.date_reconciled));
508  }
509  if (record.split_reconcile_present)
510  {
511  xaccSplitSetReconcile(split, record.split_reconcile);
512  }
513 
514  if (record.amount_present)
515  {
516  xaccSplitSetAmount(split, record.amount);
517  }
518  if (record.value_present)
519  {
520  xaccSplitSetValue(split, record.value);
521  }
522  }
523  first_record = FALSE;
524  break;
525  }
526  }
527  else
528  {
529  PERR("Corrupted record");
530  }
531  }
532  else /* The record ended */
533  {
534  record_ended = TRUE;
535  DEBUG("process_trans_record(): Record ended\n");
536  if (trans != NULL) /*If we played with a transaction, commit it here*/
537  {
538  xaccTransScrubCurrency(trans);
539  xaccTransSetReadOnly(trans, trans_ro);
540  xaccTransCommitEdit(trans);
541  g_free(trans_ro);
542  }
543  }
544  }
545 }
546 
548 {
549  char *selected_filename;
550  char *default_dir;
551  char read_buf[256];
552  char *read_retval;
553  GtkFileFilter *filter;
554  FILE *log_file;
555  char * record_start_str = "===== START";
556  /* NOTE: This string must match src/engine/TransLog.c (sans newline) */
557  char * expected_header_orig = "mod\ttrans_guid\tsplit_guid\ttime_now\t"
558  "date_entered\tdate_posted\tacc_guid\tacc_name\tnum\tdescription\t"
559  "notes\tmemo\taction\treconciled\tamount\tvalue\tdate_reconciled";
560  static char *expected_header = NULL;
561 
562  /* Use g_strdup_printf so we don't get accidental tab -> space conversion */
563  if (!expected_header)
564  expected_header = g_strdup(expected_header_orig);
565 
566  qof_log_set_level(GNC_MOD_IMPORT, QOF_LOG_DEBUG);
567  ENTER(" ");
568 
569  /* Don't log the log replay. This would only result in redundant logs */
570  xaccLogDisable();
571 
572  default_dir = gnc_get_default_directory(GNC_PREFS_GROUP);
573 
574  filter = gtk_file_filter_new();
575  gtk_file_filter_set_name(filter, "*.log");
576  gtk_file_filter_add_pattern(filter, "*.[Ll][Oo][Gg]");
577  selected_filename = gnc_file_dialog(_("Select a .log file to replay"),
578  g_list_prepend(NULL, filter),
579  default_dir,
580  GNC_FILE_DIALOG_OPEN);
581  g_free(default_dir);
582 
583  if (selected_filename != NULL)
584  {
585  /* Remember the directory as the default. */
586  default_dir = g_path_get_dirname(selected_filename);
587  gnc_set_default_directory(GNC_PREFS_GROUP, default_dir);
588  g_free(default_dir);
589 
590  /*strncpy(file,selected_filename, 255);*/
591  DEBUG("Filename found: %s", selected_filename);
592  if (xaccFileIsCurrentLog(selected_filename))
593  {
594  g_warning("Cannot open the current log file: %s", selected_filename);
595  gnc_error_dialog(NULL,
596  /* Translators: %s is the file name. */
597  _("Cannot open the current log file: %s"),
598  selected_filename);
599  }
600  else
601  {
602  DEBUG("Opening selected file");
603  log_file = g_fopen(selected_filename, "r");
604  if (!log_file || ferror(log_file) != 0)
605  {
606  int err = errno;
607  perror("File open failed");
608  gnc_error_dialog(NULL,
609  /* Translation note:
610  * First argument is the filename,
611  * second argument is the error.
612  */
613  _("Failed to open log file: %s: %s"),
614  selected_filename,
615  strerror(err));
616  }
617  else
618  {
619  if ((read_retval = fgets(read_buf, sizeof(read_buf), log_file)) == NULL)
620  {
621  DEBUG("Read error or EOF");
622  gnc_info_dialog(NULL, "%s",
623  _("The log file you selected was empty."));
624  }
625  else
626  {
627  if (strncmp(expected_header, read_buf, strlen(expected_header)) != 0)
628  {
629  PERR("File header not recognised:\n%s", read_buf);
630  PERR("Expected:\n%s", expected_header);
631  gnc_error_dialog(NULL, "%s",
632  _("The log file you selected cannot be read. "
633  "The file header was not recognized."));
634  }
635  else
636  {
637  do
638  {
639  read_retval = fgets(read_buf, sizeof(read_buf), log_file);
640  /*DEBUG("Chunk read: %s",read_retval);*/
641  if (strncmp(record_start_str, read_buf, strlen(record_start_str)) == 0) /* If a record started */
642  {
643  process_trans_record(log_file);
644  }
645  }
646  while (feof(log_file) == 0);
647  }
648  }
649  fclose(log_file);
650  }
651  }
652  g_free(selected_filename);
653  }
654  /* Start logging again */
655  xaccLogEnable();
656 
657  LEAVE("");
658 }
659 
660 
void xaccSplitSetValue(Split *s, gnc_numeric amt)
Definition: Split.c:1294
#define xaccTransAppendSplit(t, s)
Definition: Transaction.h:357
Transaction * xaccMallocTransaction(QofBook *book)
Definition: Transaction.c:513
void xaccTransScrubCurrency(Transaction *trans)
Definition: Scrub.c:993
gchar * gnc_timespec_to_iso8601_buff(Timespec ts, gchar *buff)
void xaccSplitSetAction(Split *split, const char *actn)
Definition: Split.c:1793
void qof_log_set_level(QofLogModule module, QofLogLevel level)
utility functions for the GnuCash UI
void xaccTransSetNotes(Transaction *trans, const char *notes)
Definition: Transaction.c:2115
void xaccLogDisable(void)
Definition: TransLog.c:93
#define DEBUG(format, args...)
Definition: qoflog.h:255
.log replay module interface
gboolean string_to_guid(const gchar *string, GncGUID *guid)
void xaccTransSetDescription(Transaction *trans, const char *desc)
Definition: Transaction.c:2085
void xaccTransSetNum(Transaction *trans, const char *xnum)
Definition: Transaction.c:2065
Use a 64-bit unsigned int timespec.
Definition: gnc-date.h:299
void xaccSplitSetReconcile(Split *split, char recn)
Definition: Split.c:1826
gchar * guid_to_string_buff(const GncGUID *guid, gchar *buff)
gchar * gnc_numeric_to_string(gnc_numeric n)
#define PERR(format, args...)
Definition: qoflog.h:237
#define ENTER(format, args...)
Definition: qoflog.h:261
gboolean string_to_gnc_numeric(const gchar *str, gnc_numeric *n)
void xaccTransSetDateEnteredTS(Transaction *trans, const Timespec *ts)
Definition: Transaction.c:1988
Definition: guid.h:65
void xaccTransSetCurrency(Transaction *trans, gnc_commodity *curr)
Definition: Transaction.c:1354
void xaccTransDestroy(Transaction *trans)
Definition: Transaction.c:1402
#define PWARN(format, args...)
Definition: qoflog.h:243
Transaction * xaccTransLookup(const GncGUID *guid, QofBook *book)
Definition: Transaction.c:1024
convert single-entry accounts to clean double-entry
void xaccSplitSetAmount(Split *s, gnc_numeric amt)
Definition: Split.c:1258
Account handling public routines.
void xaccTransSetReadOnly(Transaction *trans, const char *reason)
Definition: Transaction.c:2039
void xaccSplitSetMemo(Split *split, const char *memo)
Definition: Split.c:1774
void gnc_file_log_replay(void)
void xaccTransCommitEdit(Transaction *trans)
Definition: Transaction.c:1579
void xaccTransBeginEdit(Transaction *trans)
Definition: Transaction.c:1380
const char * xaccTransGetReadOnly(const Transaction *trans)
Definition: Transaction.c:2313
Timespec gnc_iso8601_to_timespec_gmt(const gchar *)
Split * xaccMallocSplit(QofBook *book)
Definition: Split.c:582
gnc_commodity * gnc_account_or_default_currency(const Account *account, gboolean *currency_from_account_found)
Definition: gnc-ui-util.c:944
API for the transaction logger.
Definition: SplitP.h:71
gnc_commodity * xaccTransGetCurrency(const Transaction *trans)
Definition: Transaction.c:1348
#define xaccAccountInsertSplit(acc, s)
Definition: Account.h:972
#define LEAVE(format, args...)
Definition: qoflog.h:271
gboolean xaccFileIsCurrentLog(const gchar *name)
Definition: TransLog.c:139
void xaccSplitSetDateReconciledTS(Split *split, Timespec *ts)
Definition: Split.c:1865
API for Transactions and Splits (journal entries)
void xaccLogEnable(void)
Definition: TransLog.c:97
const gchar * QofLogModule
Definition: qofid.h:89
void xaccTransSetDatePostedTS(Transaction *trans, const Timespec *ts)
Definition: Transaction.c:1970