Example #1
0
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()