1 /* HomeBank -- Free, easy, personal accounting for everyone.
2 * Copyright (C) 1995-2019 Maxime DOYEN
4 * This file is part of HomeBank.
6 * HomeBank is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * HomeBank 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.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "hb-import.h"
25 #include <libofx/libofx.h>
29 /****************************************************************************/
31 /****************************************************************************/
40 /* our global datas */
41 extern struct HomeBank
*GLOBALS
;
42 extern struct Preferences
*PREFS
;
48 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
51 * ofx_proc_account_cb:
53 * The ofx_proc_account_cb event is always generated first, to allow the application to create accounts
54 * or ask the user to match an existing account before the ofx_proc_statement and ofx_proc_transaction
55 * event are received. An OfxAccountData is passed to this event.
58 static LibofxProcStatementCallback
59 ofx_proc_account_cb(const struct OfxAccountData data
, ImportContext
*ctx
)
64 DB( g_print("** ofx_proc_account_cb()\n") );
66 if(data
.account_id_valid
==true)
68 DB( g_print(" account_id: %s\n", data
.account_id
) );
69 DB( g_print(" account_name: %s\n", data
.account_name
) );
72 //if(data.account_number_valid==true)
74 DB( g_print(" account_number: %s\n", data
.account_number
) );
78 if(data
.account_type_valid
==true)
80 DB( g_print(" account_type: %d\n", data
.account_type
) );
83 OFX_CHECKING A standard checking account
84 OFX_SAVINGS A standard savings account
85 OFX_MONEYMRKT A money market account
86 OFX_CREDITLINE A line of credit
87 OFX_CMA Cash Management Account
88 OFX_CREDITCARD A credit card account
89 OFX_INVESTMENT An investment account
93 if(data
.currency_valid
==true)
95 DB( g_print(" currency: %s\n", data
.currency
) );
98 //todo: normally should check for validity here
99 // in every case we create an account here
100 DB( g_print(" -> create generic account: '%s':'%s'\n", data
.account_id
, data
.account_name
) );
101 genacc
= hb_import_gen_acc_get_next (ctx
, FILETYPE_OFX
, (gchar
*)data
.account_name
, (gchar
*)data
.account_id
);
102 ctx
->curr_acc_isnew
= TRUE
;
104 dst_acc
= hb_import_acc_find_existing((gchar
*)data
.account_name
, (gchar
*)data
.account_id
);
105 if( dst_acc
!= NULL
)
107 genacc
->kacc
= dst_acc
->key
;
108 ctx
->curr_acc_isnew
= FALSE
;
109 if(dst_acc
->type
== ACC_TYPE_CREDITCARD
)
110 genacc
->is_ccard
= TRUE
;
113 ctx
->curr_acc
= genacc
;
115 DB( fputs("\n",stdout
) );
121 * ofx_proc_statement_cb:
123 * The ofx_proc_statement_cb event is sent after all ofx_proc_transaction events have been sent.
124 * An OfxStatementData is passed to this event.
127 static LibofxProcStatementCallback
128 ofx_proc_statement_cb(const struct OfxStatementData data
, ImportContext
*ctx
)
130 DB( g_print("** ofx_proc_statement_cb()\n") );
133 if(data
.ledger_balance_date_valid
==true)
137 temp_tm
= *localtime(&(data
.ledger_balance_date
));
138 g_print("ledger_balance_date : %d%s%d%s%d%s", temp_tm
.tm_mday
, "/", temp_tm
.tm_mon
+1, "/", temp_tm
.tm_year
+1900, "\n");
142 if(data
.ledger_balance_valid
==true)
144 if( ctx
->curr_acc
!= NULL
&& ctx
->curr_acc_isnew
== TRUE
)
146 ctx
->curr_acc
->initial
= data
.ledger_balance
;
148 DB( g_print("ledger_balance: $%.2f%s",data
.ledger_balance
,"\n") );
155 * ofx_proc_statement_cb:
157 * An ofx_proc_transaction_cb event is generated for every transaction in the ofx response,
158 * after ofx_proc_statement (and possibly ofx_proc_security is generated.
159 * An OfxTransactionData structure is passed to this event.
162 static LibofxProcStatementCallback
163 ofx_proc_transaction_cb(const struct OfxTransactionData data
, ImportContext
*ctx
)
169 DB( g_print("** ofx_proc_transaction_cb()\n") );
171 gentxn
= da_gen_txn_malloc();
175 if(data
.date_posted_valid
&& (data
.date_posted
!= 0))
177 temp_tm
= localtime(&data
.date_posted
);
180 g_date_set_dmy(&date
, temp_tm
->tm_mday
, temp_tm
->tm_mon
+1, temp_tm
->tm_year
+1900);
181 gentxn
->julian
= g_date_get_julian(&date
);
184 else if (data
.date_initiated_valid
&& (data
.date_initiated
!= 0))
186 temp_tm
= localtime(&data
.date_initiated
);
189 g_date_set_dmy(&date
, temp_tm
->tm_mday
, temp_tm
->tm_mon
+1, temp_tm
->tm_year
+1900);
190 gentxn
->julian
= g_date_get_julian(&date
);
195 if(data
.amount_valid
==true)
197 gentxn
->amount
= data
.amount
;
201 // check number :: The check number is most likely an integer and can probably be converted properly with atoi().
202 //However the spec allows for up to 12 digits, so it is not garanteed to work
203 if(data
.check_number_valid
==true)
205 gentxn
->rawinfo
= g_strdup(data
.check_number
);
207 //todo: reference_number ?Might present in addition to or instead of a check_number. Not necessarily a number
209 // ofx:name = Can be the name of the payee or the description of the transaction
210 if(data
.name_valid
==true)
212 gentxn
->rawpayee
= g_strdup(data
.name
);
215 //memo ( new for v4.2) #319202 Extra information not included in name
217 DB( g_print(" -> memo is='%d'\n", data
.memo_valid
) );
220 if(data
.memo_valid
==true)
222 gentxn
->rawmemo
= g_strdup(data
.memo
);
226 if(data
.transactiontype_valid
==true)
228 switch(data
.transactiontype
)
232 if(gentxn
->amount
< 0)
233 gentxn
->amount
*= -1;
236 if(gentxn
->amount
> 0)
237 gentxn
->amount
*= -1;
240 gentxn
->paymode
= PAYMODE_XFER
;
243 gentxn
->paymode
= PAYMODE_XFER
;
246 gentxn
->paymode
= PAYMODE_FEE
;
249 gentxn
->paymode
= PAYMODE_XFER
;
252 gentxn
->paymode
= PAYMODE_DEPOSIT
;
255 gentxn
->paymode
= PAYMODE_CASH
;
258 if(ctx
->curr_acc
&& ctx
->curr_acc
->is_ccard
== TRUE
)
259 gentxn
->paymode
= PAYMODE_CCARD
;
261 gentxn
->paymode
= PAYMODE_DCARD
;
264 gentxn
->paymode
= PAYMODE_XFER
;
267 gentxn
->paymode
= PAYMODE_CHECK
;
270 gentxn
->paymode
= PAYMODE_EPAYMENT
;
273 gentxn
->paymode
= PAYMODE_CASH
;
276 gentxn
->paymode
= PAYMODE_DEPOSIT
;
278 case OFX_DIRECTDEBIT
:
279 gentxn
->paymode
= PAYMODE_XFER
;
282 gentxn
->paymode
= PAYMODE_REPEATPMT
;
295 gentxn
->account
= g_strdup(ctx
->curr_acc
->name
);
297 /* ensure utf-8 here, has under windows, libofx not always return utf-8 as it should */
299 DB( g_print(" ensure UTF-8\n") );
301 gentxn
->rawinfo
= homebank_utf8_ensure(gentxn
->rawinfo
);
302 gentxn
->rawmemo
= homebank_utf8_ensure(gentxn
->rawmemo
);
303 gentxn
->rawpayee
= homebank_utf8_ensure(gentxn
->rawpayee
);
306 da_gen_txn_append(ctx
, gentxn
);
308 DB( g_print(" insert gentxn: acc=%s\n", gentxn
->account
) );
310 if( ctx
->curr_acc_isnew
== TRUE
)
312 DB( g_print(" sub amount from initial\n") );
313 ctx
->curr_acc
->initial
-= data
.amount
;
318 da_gen_txn_free(gentxn
);
319 DB( g_print(" no account, insert txn skipped\n") );
327 static LibofxProcStatusCallback
328 ofx_proc_status_cb(const struct OfxStatusData data
, ImportContext
*ctx
)
330 DB( g_print("** ofx_proc_status_cb()\n") );
332 if(data
.ofx_element_name_valid
==true){
333 DB( g_print(" Ofx entity this status is relevent to: '%s'\n", data
.ofx_element_name
) );
335 if(data
.severity_valid
==true){
336 DB( g_print(" Severity: ") );
337 switch(data
.severity
){
338 case INFO
: DB( g_print("INFO\n") );
340 case WARN
: DB( g_print("WARN\n") );
342 case ERROR
: DB( g_print("ERROR\n") );
344 default: DB( g_print("WRITEME: Unknown status severity!\n") );
347 if(data
.code_valid
==true){
348 DB( g_print(" Code: %d, name: %s\n Description: %s\n", data
.code
, data
.name
, data
.description
) );
350 if(data
.server_message_valid
==true){
351 DB( g_print(" Server Message: %s\n", data
.server_message
) );
359 GList
*homebank_ofx_import(ImportContext
*ictx
, GenFile
*genfile
)
361 /*extern int ofx_PARSER_msg;
362 extern int ofx_DEBUG_msg;
363 extern int ofx_WARNING_msg;
364 extern int ofx_ERROR_msg;
365 extern int ofx_INFO_msg;
366 extern int ofx_STATUS_msg;*/
368 DB( g_print("\n[import] ofx import (libofx=%s) \n", LIBOFX_VERSION_RELEASE_STRING
) );
370 /*ofx_PARSER_msg = false;
371 ofx_DEBUG_msg = false;
372 ofx_WARNING_msg = false;
373 ofx_ERROR_msg = false;
374 ofx_INFO_msg = false;
375 ofx_STATUS_msg = false;*/
377 LibofxContextPtr libofx_context
= libofx_get_new_context();
379 ofx_set_status_cb (libofx_context
, (LibofxProcStatusCallback
) ofx_proc_status_cb
, ictx
);
380 ofx_set_statement_cb (libofx_context
, (LibofxProcStatementCallback
) ofx_proc_statement_cb
, ictx
);
381 ofx_set_account_cb (libofx_context
, (LibofxProcAccountCallback
) ofx_proc_account_cb
, ictx
);
382 ofx_set_transaction_cb(libofx_context
, (LibofxProcTransactionCallback
)ofx_proc_transaction_cb
, ictx
);
385 //#932959: windows don't like utf8 path, so convert
386 gchar
*filepath
= g_win32_locale_filename_from_utf8(genfile
->filepath
);
387 libofx_proc_file(libofx_context
, filepath
, AUTODETECT
);
390 libofx_proc_file(libofx_context
, genfile
->filepath
, AUTODETECT
);
393 libofx_free_context(libofx_context
);
395 DB( g_print("ofx nb txn=%d\n", g_list_length(ictx
->gen_lst_txn
) ));
397 return ictx
->gen_lst_txn
;