diff options
-rw-r--r-- | Makefile.am | 8 | ||||
-rw-r--r-- | Makefile.lt | 14 | ||||
-rwxr-xr-x | acprep | 1 | ||||
-rw-r--r-- | amount.cc | 6 | ||||
-rw-r--r-- | configure.in | 29 | ||||
-rw-r--r-- | ledger.h | 1 | ||||
-rw-r--r-- | main.cc | 14 | ||||
-rw-r--r-- | ofx.cc | 247 | ||||
-rw-r--r-- | ofx.h | 21 | ||||
-rw-r--r-- | py_eval.cc | 13 |
10 files changed, 348 insertions, 6 deletions
diff --git a/Makefile.am b/Makefile.am index eea0b79d..07630668 100644 --- a/Makefile.am +++ b/Makefile.am @@ -23,6 +23,10 @@ if HAVE_XMLPARSE libledger_a_CXXFLAGS += -DHAVE_XMLPARSE=1 libledger_a_SOURCES += gnucash.cc xml.cc endif +if HAVE_LIBOFX +libledger_a_CXXFLAGS += -DHAVE_LIBOFX=1 +libledger_a_SOURCES += ofx.cc +endif if HAVE_BOOST_PYTHON libledger_a_CXXFLAGS += -DUSE_BOOST_PYTHON=1 libledger_a_SOURCES += py_eval.cc @@ -77,6 +81,10 @@ if HAVE_XMLPARSE ledger_CXXFLAGS += -DHAVE_XMLPARSE=1 ledger_LDADD += -lxmlparse -lxmltok endif +if HAVE_LIBOFX +ledger_CXXFLAGS += -DHAVE_LIBOFX=1 +ledger_LDADD += -lofx +endif if DEBUG ledger_CXXFLAGS += -DDEBUG_LEVEL=4 endif diff --git a/Makefile.lt b/Makefile.lt index cb87e5f1..8911ecf1 100644 --- a/Makefile.lt +++ b/Makefile.lt @@ -12,6 +12,7 @@ libledger_la_SOURCES = \ mask.cc \ option.cc \ parser.cc \ + reconcile.cc \ qif.cc \ quotes.cc \ textual.cc \ @@ -22,6 +23,10 @@ if HAVE_XMLPARSE libledger_la_CXXFLAGS += -DHAVE_XMLPARSE=1 libledger_la_SOURCES += gnucash.cc xml.cc endif +if HAVE_LIBOFX +libledger_la_CXXFLAGS += -DHAVE_LIBOFX=1 +libledger_la_SOURCES += ofx.cc +endif if HAVE_BOOST_PYTHON libledger_la_CXXFLAGS += -DUSE_BOOST_PYTHON=1 libledger_la_SOURCES += py_eval.cc @@ -51,6 +56,7 @@ pkginclude_HEADERS = \ parser.h \ py_eval.h \ pyledger.h \ + reconcile.h \ qif.h \ quotes.h \ textual.h \ @@ -72,6 +78,14 @@ ledger_LDADD = $(LIBOBJS) libledger.la -lboost_python -lpython$(PYTHON_VERSION) else ledger_LDADD = $(LIBOBJS) libledger.la endif +if HAVE_XMLPARSE +ledger_CXXFLAGS += -DHAVE_XMLPARSE=1 +ledger_LDADD += -lxmlparse -lxmltok +endif +if HAVE_LIBOFX +ledger_CXXFLAGS += -DHAVE_LIBOFX=1 +ledger_LDADD += -lofx +endif if DEBUG ledger_CXXFLAGS += -DDEBUG_LEVEL=4 endif @@ -12,6 +12,7 @@ fi autoconf INCDIRS="-I/sw/include -I/usr/include/httpd/xml -I/sw/include/python2.3" +INCDIRS="$INCDIRS -I/sw/include/libofx" INCDIRS="$INCDIRS -Wno-long-double" LIBDIRS="-L/sw/lib -L/sw/lib/python2.3/config" @@ -145,7 +145,7 @@ amount_t::amount_t(const long value) quantity = new bigint_t; mpz_set_si(MPZ(quantity), value); } else { - quantity = NULL; + quantity = NULL; } commodity_ = NULL; } @@ -156,7 +156,7 @@ amount_t::amount_t(const unsigned long value) quantity = new bigint_t; mpz_set_ui(MPZ(quantity), value); } else { - quantity = NULL; + quantity = NULL; } commodity_ = NULL; } @@ -167,7 +167,7 @@ amount_t::amount_t(const double value) quantity = new bigint_t; mpz_set_d(MPZ(quantity), value); } else { - quantity = NULL; + quantity = NULL; } commodity_ = NULL; } diff --git a/configure.in b/configure.in index 7b00d506..3ea18391 100644 --- a/configure.in +++ b/configure.in @@ -111,6 +111,35 @@ else AM_CONDITIONAL(HAVE_XMLPARSE, false) fi +# check for libofx +AC_ARG_ENABLE(ofx, + [ --enable-ofx Turn on support for OFX/OCF parsing], + [case "${enableval}" in + yes) ofx=true ;; + no) ofx=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-ofx) ;; + esac],[ofx=true]) +AM_CONDITIONAL(USE_OFX, test x$ofx = xtrue) + +if [test x$ofx = xtrue ]; then + AC_CACHE_CHECK( + [if libofx is available], + [libofx_avail], + [libofx_save_libs=$LIBS + LIBS="-lofx $LIBS" + AC_LANG_PUSH(C++) + AC_TRY_LINK( + [#include <libofx.h>], + [ LibofxContextPtr libofx_context = libofx_get_new_context();], + [libofx_avail=true], + [libofx_avail=false]) + AC_LANG_POP + LIBS=$libofx_save_libs]) + AM_CONDITIONAL(HAVE_LIBOFX, test x$libofx_avail = xtrue) +else + AM_CONDITIONAL(HAVE_LIBOFX, false) +fi + # check for Python AC_ARG_ENABLE(python, [ --enable-python Turn on Python support], @@ -33,5 +33,6 @@ #include <xml.h> #include <gnucash.h> #include <qif.h> +#include <ofx.h> #endif // _LEDGER_H @@ -256,17 +256,27 @@ int parse_and_report(int argc, char * argv[], char * envp[]) // Parse initialization files, ledger data, price database, etc. std::auto_ptr<binary_parser_t> bin_parser(new binary_parser_t); +#ifdef READ_GNUCASH + std::auto_ptr<gnucash_parser_t> gnucash_parser(new gnucash_parser_t); +#endif #ifdef HAVE_XMLPARSE std::auto_ptr<xml_parser_t> xml_parser(new xml_parser_t); - std::auto_ptr<gnucash_parser_t> gnucash_parser(new gnucash_parser_t); +#endif +#ifdef HAVE_LIBOFX + std::auto_ptr<ofx_parser_t> ofx_parser(new ofx_parser_t); #endif std::auto_ptr<qif_parser_t> qif_parser(new qif_parser_t); std::auto_ptr<textual_parser_t> text_parser(new textual_parser_t); register_parser(bin_parser.get()); +#ifdef READ_GNUCASH + register_parser(gnucash_parser.get()); +#endif #ifdef HAVE_XMLPARSE register_parser(xml_parser.get()); - register_parser(gnucash_parser.get()); +#endif +#ifdef HAVE_LIBOFX + register_parser(ofx_parser.get()); #endif register_parser(qif_parser.get()); register_parser(text_parser.get()); @@ -0,0 +1,247 @@ +#include "journal.h" +#include "ofx.h" +#include "format.h" +#include "datetime.h" +#include "error.h" +#include "debug.h" +#include "util.h" + +#include <libofx.h> + +namespace ledger { + +#define MAX_LINE 1024 + +static char line[MAX_LINE + 1]; +static std::string path; +static unsigned int linenum; + +typedef std::map<const std::string, account_t *> accounts_map; +typedef std::pair<const std::string, account_t *> accounts_pair; + +typedef std::map<const std::string, commodity_t *> commodities_map; +typedef std::pair<const std::string, commodity_t *> commodities_pair; + +journal_t * curr_journal; +accounts_map ofx_accounts; +commodities_map ofx_account_currencies; +commodities_map ofx_securities; +account_t * master_account; + +int ofx_proc_statement_cb(struct OfxStatementData data, void * statement_data) +{ +} + +int ofx_proc_account_cb(struct OfxAccountData data, void * account_data) +{ + if (! data.account_id_valid) + return -1; + + DEBUG_PRINT("ledger.ofx.parse", "account " << data.account_name); + account_t * account = new account_t(master_account, data.account_name); + curr_journal->add_account(account); + ofx_accounts.insert(accounts_pair(data.account_id, account)); + + if (data.currency_valid) { + commodity_t * commodity = commodity_t::find_commodity(data.currency, true); + commodity->flags |= COMMODITY_STYLE_SUFFIXED | COMMODITY_STYLE_SEPARATED; + + commodities_map::iterator i = ofx_account_currencies.find(data.account_id); + if (i == ofx_account_currencies.end()) + ofx_account_currencies.insert(commodities_pair(data.account_id, + commodity)); + } + + return 0; +} + +int ofx_proc_transaction_cb(struct OfxTransactionData data, + void * transaction_data) +{ + if (! data.account_id_valid || ! data.units_valid) + return -1; + + accounts_map::iterator i = ofx_accounts.find(data.account_id); + assert(i != ofx_accounts.end()); + account_t * account = (*i).second; + + entry_t * entry = new entry_t; + + entry->add_transaction(new transaction_t(account)); + transaction_t * xact = entry->transactions.back(); + + // get the account's default currency + commodities_map::iterator ac = ofx_account_currencies.find(data.account_id); + assert(ac != ofx_account_currencies.end()); + commodity_t * default_commodity = (*ac).second; + + std::string buf; + std::ostringstream stream(buf); + stream << - data.units; + + // jww (2005-02-09): what if the amount contains fees? + + if (data.unique_id_valid) { + commodities_map::iterator s = ofx_securities.find(data.unique_id); + assert(s != ofx_securities.end()); + xact->amount = stream.str() + " " + (*s).second->symbol; + } else { + xact->amount = stream.str() + " " + default_commodity->symbol; + } + + if (data.unitprice_valid && data.unitprice != 1.0) { + std::ostringstream cstream(buf); + stream << - data.unitprice; + xact->cost = new amount_t(stream.str() + " " + default_commodity->symbol); + } + + DEBUG_PRINT("ledger.ofx.parse", "xact " << xact->amount + << " from " << *xact->account); + + if (data.date_initiated_valid) + entry->date = data.date_initiated; + else if (data.date_posted_valid) + entry->date = data.date_posted; + + if (data.check_number_valid) + entry->code = data.check_number; + else if (data.reference_number_valid) + entry->code = data.reference_number; + + if (data.name_valid) + entry->payee = data.name; + + if (data.memo_valid) + xact->note = data.memo; + + // jww (2005-02-09): check for fi_id_corrected? or is this handled + // by the library? + + // Balance all entries into <Unknown>, since it is not specified. + account = curr_journal->find_account("<Unknown>"); + entry->add_transaction(new transaction_t(account)); + + if (! curr_journal->add_entry(entry)) { + print_entry(std::cerr, *entry); + // jww (2005-02-09): uncomment + //have_error = "The above entry does not balance"; + delete entry; + return -1; + } + return 0; +} + +int ofx_proc_security_cb(struct OfxSecurityData data, void * security_data) +{ + if (! data.unique_id_valid) + return -1; + + std::string symbol; + if (data.ticker_valid) + symbol = data.ticker; + else if (data.currency_valid) + symbol = data.currency; + else + return -1; + + commodity_t * commodity = commodity_t::find_commodity(symbol, true); + commodity->flags |= COMMODITY_STYLE_SUFFIXED | COMMODITY_STYLE_SEPARATED; + + if (data.secname_valid) + commodity->name = data.secname; + + if (data.memo_valid) + commodity->note = data.memo; + + commodities_map::iterator i = ofx_securities.find(data.unique_id); + if (i == ofx_securities.end()) { + DEBUG_PRINT("ledger.ofx.parse", "security " << symbol); + ofx_securities.insert(commodities_pair(data.unique_id, commodity)); + } + + // jww (2005-02-09): What is the commodity for data.unitprice? + if (data.date_unitprice_valid && data.unitprice_valid) { + DEBUG_PRINT("ledger.ofx.parse", " price " << data.unitprice); + commodity->add_price(data.date_unitprice, amount_t(data.unitprice)); + } + + return 0; +} + +int ofx_proc_status_cb(struct OfxStatusData data, void * status_data) +{ +} + +bool ofx_parser_t::test(std::istream& in) const +{ + char buf[256]; + + in.getline(buf, 255); + if (std::strncmp(buf, "OFXHEADER", 9) == 0) { + in.seekg(0, std::ios::beg); + return true; + } + else if (std::strncmp(buf, "<?xml", 5) != 0) { + in.seekg(0, std::ios::beg); + return false; + } + + in.getline(buf, 255); + if (std::strncmp(buf, "<?OFX", 5) != 0 && + std::strncmp(buf, "<?ofx", 5) != 0) { + in.seekg(0, std::ios::beg); + return false; + } + + in.seekg(0, std::ios::beg); + return true; +} + +unsigned int ofx_parser_t::parse(std::istream& in, + journal_t * journal, + account_t * master, + const std::string * original_file) +{ + if (! original_file) + return 0; + + curr_journal = journal; + master_account = master ? master : journal->master; + + LibofxContextPtr libofx_context = libofx_get_new_context(); + + ofx_set_statement_cb (libofx_context, ofx_proc_statement_cb, 0); + ofx_set_account_cb (libofx_context, ofx_proc_account_cb, 0); + ofx_set_transaction_cb(libofx_context, ofx_proc_transaction_cb, 0); + ofx_set_security_cb (libofx_context, ofx_proc_security_cb, 0); + ofx_set_status_cb (libofx_context, ofx_proc_status_cb, 0); + + // The processing is done by way of callbacks, which are all defined + // above. + libofx_proc_file(libofx_context, original_file->c_str(), AUTODETECT); + + libofx_free_context(libofx_context); + + return 1; // jww (2005-02-09): count; +} + +} // namespace ledger + +#ifdef USE_BOOST_PYTHON + +#include <boost/python.hpp> + +using namespace boost::python; +using namespace ledger; + +BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(ofx_parse_overloads, + ofx_parser_t::parse, 2, 4) + +void export_ofx() { + class_< ofx_parser_t, bases<parser_t> > ("OfxParser") + .def("test", &ofx_parser_t::test) + .def("parse", &ofx_parser_t::parse, ofx_parse_overloads()) + ; +} + +#endif // USE_BOOST_PYTHON @@ -0,0 +1,21 @@ +#ifndef _OFX_H +#define _OFX_H + +#include "parser.h" + +namespace ledger { + +class ofx_parser_t : public parser_t +{ + public: + virtual bool test(std::istream& in) const; + + virtual unsigned int parse(std::istream& in, + journal_t * journal, + account_t * master = NULL, + const std::string * original_file = NULL); +}; + +} // namespace ledger + +#endif // _OFX_H @@ -19,10 +19,15 @@ void export_parser(); void export_textual(); void export_binary(); void export_qif(); -#ifdef HAVE_XMLPARSE +#ifdef READ_GNUCASH void export_gnucash(); +#endif +#ifdef HAVE_XMLPARSE void export_xml(); #endif +#ifdef HAVE_LIBOFX +void export_ofx(); +#endif void export_option(); void export_config(); void export_walk(); @@ -44,6 +49,12 @@ void initialize_ledger_for_python() #ifdef READ_GNUCASH export_gnucash(); #endif +#ifdef HAVE_XMLPARSE + export_xml(); +#endif +#ifdef HAVE_LIBOFX + export_ofx(); +#endif export_option(); export_config(); export_walk(); |