29 #include <glib/gi18n.h>
30 #include <glib/gstdio.h>
37 #include "TransactionP.h"
44 #include "gnc-gui-query.h"
46 #define GNC_PREFS_GROUP "dialogs.log-replay"
49 void qof_instance_set_guid (gpointer inst,
const GncGUID *guid);
65 #define STRING_FIELD_SIZE 256
68 enum _enum_action {LOG_BEGIN_EDIT, LOG_ROLLBACK, LOG_COMMIT, LOG_DELETE} log_action;
69 int log_action_present;
71 int trans_guid_present;
73 int split_guid_present;
77 int date_entered_present;
79 int date_posted_present;
82 char acc_name[STRING_FIELD_SIZE];
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;
95 int split_reconcile_present;
101 int date_reconciled_present;
113 static char * my_strtok (s, delim)
132 s = strpbrk (token, delim);
136 olds = strchr (token,
'\0');
147 static split_record interpret_split_record(
char *record_line)
151 memset(&record, 0,
sizeof(record));
152 DEBUG(
"interpret_split_record(): Start...");
153 if (strlen(tok_ptr = my_strtok(record_line,
"\t")) != 0)
158 record.log_action = LOG_BEGIN_EDIT;
161 record.log_action = LOG_DELETE;
164 record.log_action = LOG_COMMIT;
167 record.log_action = LOG_ROLLBACK;
170 record.log_action_present = TRUE;
172 if (strlen(tok_ptr = my_strtok(NULL,
"\t")) != 0)
175 record.trans_guid_present = TRUE;
177 if (strlen(tok_ptr = my_strtok(NULL,
"\t")) != 0)
180 record.split_guid_present = TRUE;
182 if (strlen(tok_ptr = my_strtok(NULL,
"\t")) != 0)
185 record.log_date_present = TRUE;
187 if (strlen(tok_ptr = my_strtok(NULL,
"\t")) != 0)
190 record.date_entered_present = TRUE;
192 if (strlen(tok_ptr = my_strtok(NULL,
"\t")) != 0)
195 record.date_posted_present = TRUE;
197 if (strlen(tok_ptr = my_strtok(NULL,
"\t")) != 0)
200 record.acc_guid_present = TRUE;
202 if (strlen(tok_ptr = my_strtok(NULL,
"\t")) != 0)
204 strncpy(record.acc_name, tok_ptr, STRING_FIELD_SIZE - 1);
205 record.acc_name_present = TRUE;
207 if (strlen(tok_ptr = my_strtok(NULL,
"\t")) != 0)
209 strncpy(record.trans_num, tok_ptr, STRING_FIELD_SIZE - 1);
210 record.trans_num_present = TRUE;
212 if (strlen(tok_ptr = my_strtok(NULL,
"\t")) != 0)
214 strncpy(record.trans_descr, tok_ptr, STRING_FIELD_SIZE - 1);
215 record.trans_descr_present = TRUE;
217 if (strlen(tok_ptr = my_strtok(NULL,
"\t")) != 0)
219 strncpy(record.trans_notes, tok_ptr, STRING_FIELD_SIZE - 1);
220 record.trans_notes_present = TRUE;
222 if (strlen(tok_ptr = my_strtok(NULL,
"\t")) != 0)
224 strncpy(record.split_memo, tok_ptr, STRING_FIELD_SIZE - 1);
225 record.split_memo_present = TRUE;
227 if (strlen(tok_ptr = my_strtok(NULL,
"\t")) != 0)
229 strncpy(record.split_action, tok_ptr, STRING_FIELD_SIZE - 1);
230 record.split_action_present = TRUE;
232 if (strlen(tok_ptr = my_strtok(NULL,
"\t")) != 0)
234 record.split_reconcile = tok_ptr[0];
235 record.split_reconcile_present = TRUE;
237 if (strlen(tok_ptr = my_strtok(NULL,
"\t")) != 0)
240 record.amount_present = TRUE;
242 if (strlen(tok_ptr = my_strtok(NULL,
"\t")) != 0)
245 record.value_present = TRUE;
247 if (strlen(tok_ptr = my_strtok(NULL,
"\t")) != 0)
250 record.date_reconciled_present = TRUE;
253 if (strlen(tok_ptr = my_strtok(NULL,
"\t")) != 0)
255 PERR(
"interpret_split_record(): Expected number of fields exceeded!");
257 DEBUG(
"interpret_split_record(): End");
263 char * string_ptr = NULL;
264 char string_buf[256];
266 DEBUG(
"dump_split_record(): Start...");
267 if (record.log_action_present)
269 switch (record.log_action)
272 DEBUG(
"Log action: LOG_BEGIN_EDIT");
275 DEBUG(
"Log action: LOG_DELETE");
278 DEBUG(
"Log action: LOG_COMMIT");
281 DEBUG(
"Log action: LOG_ROLLBACK");
285 if (record.trans_guid_present)
288 DEBUG(
"Transaction GncGUID: %s", string_buf);
290 if (record.split_guid_present)
293 DEBUG(
"Split GncGUID: %s", string_buf);
295 if (record.log_date_present)
298 DEBUG(
"Log entry date: %s", string_buf);
300 if (record.date_entered_present)
303 DEBUG(
"Date entered: %s", string_buf);
305 if (record.date_posted_present)
308 DEBUG(
"Date posted: %s", string_buf);
310 if (record.acc_guid_present)
313 DEBUG(
"Account GncGUID: %s", string_buf);
315 if (record.acc_name_present)
317 DEBUG(
"Account name: %s", record.acc_name);
319 if (record.trans_num_present)
321 DEBUG(
"Transaction number: %s", record.trans_num);
323 if (record.trans_descr_present)
325 DEBUG(
"Transaction description: %s", record.trans_descr);
327 if (record.trans_notes_present)
329 DEBUG(
"Transaction notes: %s", record.trans_notes);
331 if (record.split_memo_present)
333 DEBUG(
"Split memo: %s", record.split_memo);
335 if (record.split_action_present)
337 DEBUG(
"Split action: %s", record.split_action);
339 if (record.split_reconcile_present)
341 DEBUG(
"Split reconcile: %c", record.split_reconcile);
343 if (record.amount_present)
346 DEBUG(
"Record amount: %s", string_ptr);
349 if (record.value_present)
352 DEBUG(
"Record value: %s", string_ptr);
355 if (record.date_reconciled_present)
358 DEBUG(
"Reconciled date: %s", string_buf);
363 static void process_trans_record( FILE *log_file)
367 char * trans_ro = NULL;
368 const char * record_end_str =
"===== END";
369 int first_record = TRUE;
370 int record_ended = FALSE;
374 Split * split = NULL;
376 QofBook * book = gnc_get_current_book();
378 DEBUG(
"process_trans_record(): Begin...\n");
380 while ( record_ended == FALSE)
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)
387 record = interpret_split_record( read_buf);
388 dump_split_record( record);
389 if (record.log_action_present)
391 switch (record.log_action)
394 DEBUG(
"process_trans_record():Ignoring log action: LOG_BEGIN_EDIT");
397 DEBUG(
"process_trans_record():Ignoring log action: LOG_ROLLBACK");
400 DEBUG(
"process_trans_record(): Playing back LOG_DELETE");
402 && first_record == TRUE)
404 first_record = FALSE;
407 PWARN(
"Destroying a read only transaction.");
408 xaccTransClearReadOnly(trans);
413 else if (first_record == TRUE)
415 PERR(
"The transaction to delete was not found!");
421 DEBUG(
"process_trans_record(): Playing back LOG_COMMIT");
422 if (record.trans_guid_present == TRUE
423 && first_record == TRUE)
425 trans = xaccTransLookupDirect (record.trans_guid, book);
428 DEBUG(
"process_trans_record(): Transaction to be edited was found");
433 PWARN(
"Replaying a read only transaction.");
434 xaccTransClearReadOnly(trans);
439 DEBUG(
"process_trans_record(): Creating a new transaction");
444 qof_instance_set_guid (QOF_INSTANCE (trans),
445 &(record.trans_guid));
447 if (record.date_entered_present)
451 if (record.date_posted_present)
455 if (record.trans_num_present)
459 if (record.trans_descr_present)
463 if (record.trans_notes_present)
468 if (record.split_guid_present == TRUE)
470 gboolean is_new_split;
472 split = xaccSplitLookupDirect (record.split_guid, book);
475 DEBUG(
"process_trans_record(): Split to be edited was found");
476 is_new_split = FALSE;
480 DEBUG(
"process_trans_record(): Creating a new split");
484 xaccSplitSetGUID (split, &(record.split_guid));
485 if (record.acc_guid_present)
487 acct = xaccAccountLookupDirect(record.acc_guid, book);
497 if (record.split_memo_present)
501 if (record.split_action_present)
505 if (record.date_reconciled_present)
509 if (record.split_reconcile_present)
514 if (record.amount_present)
518 if (record.value_present)
523 first_record = FALSE;
529 PERR(
"Corrupted record");
535 DEBUG(
"process_trans_record(): Record ended\n");
549 char *selected_filename;
553 GtkFileFilter *filter;
555 char * record_start_str =
"===== START";
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;
563 if (!expected_header)
564 expected_header = g_strdup(expected_header_orig);
572 default_dir = gnc_get_default_directory(GNC_PREFS_GROUP);
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),
580 GNC_FILE_DIALOG_OPEN);
583 if (selected_filename != NULL)
586 default_dir = g_path_get_dirname(selected_filename);
587 gnc_set_default_directory(GNC_PREFS_GROUP, default_dir);
591 DEBUG(
"Filename found: %s", selected_filename);
594 g_warning(
"Cannot open the current log file: %s", selected_filename);
595 gnc_error_dialog(NULL,
597 _(
"Cannot open the current log file: %s"),
602 DEBUG(
"Opening selected file");
603 log_file = g_fopen(selected_filename,
"r");
604 if (!log_file || ferror(log_file) != 0)
607 perror(
"File open failed");
608 gnc_error_dialog(NULL,
613 _(
"Failed to open log file: %s: %s"),
619 if ((read_retval = fgets(read_buf,
sizeof(read_buf), log_file)) == NULL)
621 DEBUG(
"Read error or EOF");
622 gnc_info_dialog(NULL,
"%s",
623 _(
"The log file you selected was empty."));
627 if (strncmp(expected_header, read_buf, strlen(expected_header)) != 0)
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."));
639 read_retval = fgets(read_buf,
sizeof(read_buf), log_file);
641 if (strncmp(record_start_str, read_buf, strlen(record_start_str)) == 0)
643 process_trans_record(log_file);
646 while (feof(log_file) == 0);
652 g_free(selected_filename);
void xaccSplitSetValue(Split *s, gnc_numeric amt)
#define xaccTransAppendSplit(t, s)
Transaction * xaccMallocTransaction(QofBook *book)
void xaccTransScrubCurrency(Transaction *trans)
gchar * gnc_timespec_to_iso8601_buff(Timespec ts, gchar *buff)
void xaccSplitSetAction(Split *split, const char *actn)
void qof_log_set_level(QofLogModule module, QofLogLevel level)
utility functions for the GnuCash UI
void xaccTransSetNotes(Transaction *trans, const char *notes)
void xaccLogDisable(void)
#define DEBUG(format, args...)
.log replay module interface
gboolean string_to_guid(const gchar *string, GncGUID *guid)
void xaccTransSetDescription(Transaction *trans, const char *desc)
void xaccTransSetNum(Transaction *trans, const char *xnum)
Use a 64-bit unsigned int timespec.
void xaccSplitSetReconcile(Split *split, char recn)
gchar * guid_to_string_buff(const GncGUID *guid, gchar *buff)
gchar * gnc_numeric_to_string(gnc_numeric n)
#define PERR(format, args...)
#define ENTER(format, args...)
gboolean string_to_gnc_numeric(const gchar *str, gnc_numeric *n)
void xaccTransSetDateEnteredTS(Transaction *trans, const Timespec *ts)
void xaccTransSetCurrency(Transaction *trans, gnc_commodity *curr)
void xaccTransDestroy(Transaction *trans)
#define PWARN(format, args...)
Transaction * xaccTransLookup(const GncGUID *guid, QofBook *book)
convert single-entry accounts to clean double-entry
void xaccSplitSetAmount(Split *s, gnc_numeric amt)
Account handling public routines.
void xaccTransSetReadOnly(Transaction *trans, const char *reason)
void xaccSplitSetMemo(Split *split, const char *memo)
void gnc_file_log_replay(void)
void xaccTransCommitEdit(Transaction *trans)
void xaccTransBeginEdit(Transaction *trans)
const char * xaccTransGetReadOnly(const Transaction *trans)
Timespec gnc_iso8601_to_timespec_gmt(const gchar *)
Split * xaccMallocSplit(QofBook *book)
gnc_commodity * gnc_account_or_default_currency(const Account *account, gboolean *currency_from_account_found)
API for the transaction logger.
gnc_commodity * xaccTransGetCurrency(const Transaction *trans)
#define xaccAccountInsertSplit(acc, s)
#define LEAVE(format, args...)
gboolean xaccFileIsCurrentLog(const gchar *name)
void xaccSplitSetDateReconciledTS(Split *split, Timespec *ts)
API for Transactions and Splits (journal entries)
const gchar * QofLogModule
void xaccTransSetDatePostedTS(Transaction *trans, const Timespec *ts)