def main(): if len(argv) < 3: print "Usage: python make_securities.py <gnucash-file> <symbols-file>" exit(1) gnucash_file = argv[1] secs = read_symbols(argv[2]) sess = Session(gnucash_file) try: # Make sure all the commodities exist ct = sess.book.get_table() created_secs = 0 updated_secs = 0 total_secs = 0 for ns in ["CUSIP", "ISIN"]: total_secs += len(secs[ns]) for c in ct.get_commodities(ns): matched_sec = None for k in secs[ns]: sec = secs[ns][k] if sec["id"] == c.get_cusip(): matched_sec = sec break if matched_sec: matched_sec["c"] = c updated = False if c.get_fullname() != matched_sec["name"]: c.set_fullname(matched_sec["name"]) updated = True if c.get_mnemonic() != matched_sec["symbol"]: c.set_mnemonic(matched_sec["symbol"]) updated = True if updated: updated_secs += 1 print "DEBUG: Updating Commodity", sec["name"] del secs[ns][matched_sec["id"]] for k in secs[ns]: sec = secs[ns][k] c = GncCommodity(sess.book, sec["name"], ns, sec["symbol"], sec["id"], 10000) if c: ct.insert(c) created_secs += 1 print "DEBUG: Created Commodity", sec["name"], sec["id"] else: print "ERROR: Error creating Commodity", sec["name"] print "INFO:", total_secs, "commodities total,", created_secs, "created,", updated_secs, "updated." wait_for_backup_file(gnucash_file) sess.save() except BaseException as e: print "ERROR:", e finally: sess.end() sess.destroy()
def main(): if len(argv) < 4: print 'Usage: python ofxml_make_commodity_accounts.py <gnucash-file> <ofxml-file> <accountmap-file>' exit(1) gnucash_file=argv[1] doc=ElementTree(file=argv[2]) acctids=doc.findall('./INVSTMTMSGSRSV1/INVSTMTTRNRS/INVSTMTRS/INVACCTFROM/ACCTID') if len(acctids)!=1: print 'ERROR: No unique account number found in OFX: found', len(acctids) return acctid=acctids[0].text.strip() acctcur='any' m=re.search('^(.*)-([A-Z]{3})$',acctid) if m: acctid=m.group(1) acctcur=m.group(2) print "INFO: Account number:", acctid, "Currency:", acctcur fitids={} for itran in doc.findall('.//INVBUY')+doc.findall('.//INVSELL')+doc.findall('.//REINVEST'): fitid=itran.find('./INVTRAN/FITID') if not fitid is None: fitid=fitid.text.strip() if fitid in fitids: print "ERROR: Non-unique FITID found:", fitid exit(1) fitids[fitid]=itran # Fantastic, the FITID is not saved by GnuCash for income transactions... # Index by (date,amount,memo) instead incometrns={} for itran in doc.findall('.//INCOME'): fields={} for path in ('INVTRAN/DTTRADE', 'INVTRAN/MEMO', 'TOTAL'): el=itran.find('./'+path) if not el is None and len(el.text.strip())>0: fields[path]=el.text.strip() if len(fields)!=3: print "ERROR: Can't create identifier for INCOME transaction, ignoring." incometrns[(fields['INVTRAN/DTTRADE'][0:8],fields['INVTRAN/MEMO'],Fraction(fields['TOTAL']))]=itran sess=Session(gnucash_file) try: # Find GNC parent account root=sess.book.get_root_account() matched_accts=find_acct_by_number_and_currency(root,acctid,acctcur) if len(matched_accts)==0: from_iban=conv_iban(acctid) if from_iban: matched_accts=find_acct_by_number_and_currency(root,from_iban,acctcur) if len(matched_accts)!=1: print 'ERROR: No unique account this number/currency; found', len(matched_accts) return acct=matched_accts[0] print 'DEBUG: Found parent account:',acct.GetName() accountmap=read_accountmap(argv[3],acct.GetCode()+'-'+acct.GetCommodity().get_mnemonic()) # Find child Stock/Mutual accounts secaccts=[] # SwigPyObject is not Hashable :( for cacct in acct.get_descendants(): atype=cacct.GetType() if atype==gnucash_core.ACCT_TYPE_STOCK or atype==gnucash_core.ACCT_TYPE_MUTUAL: secaccts.append(cacct.get_instance()) # Find income accounts incaccts=[] # SwigPyObject is not Hashable :( for typ in accountmap: if typ[0:6]=="INCOME": inst=find_acct_by_path(root,accountmap[typ]).get_instance() if not (inst is None or inst in incaccts): incaccts.append(inst) if len(incaccts)==0 and len(incometrns)>0: print 'WARNING: no income accounts defined for account',acct.GetCode()+'-'+acct.GetCommodity().get_mnemonic() print 'WARNING: income transactions will not be fixed' # Go through all transactions for tran in _py_xaccSplitListGetUniqueTransactions(acct.GetSplitList()): # Consider fixing if transaction ... # ... has exactly 2 splits # ... has 1 split with a child Stock/Mutual account # ... has 1 split with an online ID splits=tran.GetSplitList() if len(splits)==2: cashsplit=None secsplit=None incsplit=None online_id=None for split in splits: if split.GetAccount().get_instance() in secaccts: secsplit=split if split.GetAccount().get_instance() in incaccts: incsplit=split if split.GetAccount().get_instance()==acct.get_instance(): cashsplit=split oid=_py_gnc_import_get_split_online_id(sess,split) if not oid is None: if online_id is None: online_id=oid else: online_id=False if not (cashsplit is None or secsplit is None or online_id is None or online_id is False): if not online_id in fitids: # This can happen if we encounter a transaction outside of this OFX period #print 'DEBUG: FITID',online_id,'not found in OFX file.' continue fix_buysell_transaction(tran,secsplit,cashsplit,fitids[online_id],root,accountmap) elif not (cashsplit is None or incsplit is None): date=tran.RetDatePostedTS().strftime('%Y%m%d') memo=re.sub(' +',' ',tran.GetDescription()) # GnuCash importer likes to insert spaces randomly amt=gnc_numeric_to_fraction(cashsplit.GetAmount()) if not (date,memo,amt) in incometrns: # This can happen if we encounter a transaction outside of this OFX period #print "DEBUG: No match for income transaction",date,memo,amt continue fix_income_transaction(tran,incsplit,cashsplit,incometrns[(date,memo,amt)],root,accountmap) wait_for_backup_file(gnucash_file) sess.save() except BaseException as e: print 'ERROR:',e finally: sess.end() sess.destroy()
def main(): if len(argv) < 3: print 'Usage: python ofxml_make_commodity_accounts.py <gnucash-file> <ofxml-file> [symbols-file]' exit(1) gnucash_file=argv[1] symbols_file=False if len(argv)>=4: symbols_file=read_symbols(argv[3]) doc=ElementTree(file=argv[2]) acctids=doc.findall('./INVSTMTMSGSRSV1/INVSTMTTRNRS/INVSTMTRS/INVACCTFROM/ACCTID') if len(acctids)!=1: print 'ERROR: No unique account number found in OFX: found', len(acctids) return acctid=acctids[0].text.strip() acctcur='any' m=re.search('^(.*)-([A-Z]{3})$',acctid) if m: acctid=m.group(1) acctcur=m.group(2) print "INFO: Account number:", acctid, "Currency:", acctcur missing_symbols=False secs=[] for sec in doc.findall('./SECLISTMSGSRSV1/SECLIST/*/SECINFO'): id=sec.findall('./SECID/UNIQUEID')[0].text.strip() type=sec.findall('./SECID/UNIQUEIDTYPE')[0].text.strip() name=sec.findall('./SECNAME')[0].text.strip() symbol=sec.findall('./TICKER') if len(symbol): symbol=symbol[0].text.strip() else: symbol=None if symbols_file: if id in symbols_file[type]: name=symbols_file[type][id]['name'] symbol=symbols_file[type][id]['symbol'] else: print "WARNING: Missing symbol for", type, id, name, symbol missing_symbols=True secs.append({'id': id, 'type': type, 'name': name, 'symbol': symbol}) print "DEBUG: Found", len(secs), "commodities." sess=Session(gnucash_file) try: # Make sure all the commodities exist ct=sess.book.get_table() for ns in ['CUSIP','ISIN']: for c in ct.get_commodities(ns): matched_sec=None for sec in secs: if sec['type']==ns and sec['id']==c.get_cusip(): sec['c']=c break missing_secs=False for i,sec in enumerate(secs): if not 'c' in sec: print 'WARNING: Missing commodity', sec['type'],sec['id'],sec['name'],sec['symbol'] missing_secs=True if missing_secs or missing_symbols: print 'ERROR: Missing symbols or commodities, aborting.' return # Find GNC parent account root=sess.book.get_root_account() matched_accts=find_acct_by_number_and_currency(root,acctid,acctcur) if len(matched_accts)==0: from_iban=conv_iban(acctid) if from_iban: matched_accts=find_acct_by_number_and_currency(root,from_iban,acctcur) if len(matched_accts)!=1: print 'ERROR: No unique account this number/currency; found', len(matched_accts) return acct=matched_accts[0] print 'DEBUG: Found parent account:',acct.GetName() # Make sure the account has the appropriate stock accounts created_accounts=0 for sec in secs: matched_acct=None for secacct in acct.get_children(): if secacct.GetCommodity().get_instance()==sec['c'].get_instance(): matched_acct=secacct break if not matched_acct: secacct=Account(sess.book) if secacct: secacct.SetName(sec['name']) secacct.SetType(gnucash.ACCT_TYPE_STOCK) secacct.SetCommodity(sec['c']) secacct.SetCode(sec['id']) acct.append_child(secacct) created_accounts+=1 print 'DEBUG: Created Account',sec['name'] else: print 'ERROR: Error creating Account',sec['name'] print 'INFO:',len(secs),'accounts total',created_accounts,'created.' wait_for_backup_file(gnucash_file) sess.save() except BaseException as e: print 'ERROR:',e finally: sess.end() sess.destroy()