I’ve been tasked with converting a year-long record of business transactions from Gnucash to Quickbooks. Google helped me determine that the IIF format will be importable by Quickbooks, and I’ve already managed to dump the Gnucash data into Quicken Interchange Format. So now it’s a simple matter of parsing through this text file and reading off the details of each transaction.
I figured out a workable conversion program when there aren’t any splits in the transaction. However, my attempt to loop through the splits and faithfully copy the data into the IIF output file is broken. I’m hoping some python expert will be able to pinpoint the bug easily.
Here’s a sample transaction block from the QIF input file.
D03/03/2010
N6381
U-409.09
T-409.09
P51.25 cl * 10
LCost of Goods Sold:Labor:Unnamed Employee
SCost of Goods Sold:Labor:Unnamed Employee
$-512.50
SExpenses:Taxes:FICA Payable
$39.20
SExpenses:Taxes:Federal Payroll Tax Payable
$29.00
SExpenses:Taxes:State Payroll Tax Payable
$35.21
^
Here’s the function in my python program that reads such a block. It’s more wasteful of memory than reading line-by-line and defining the relevant variables based on the first character in the current line. I tried that method too, but I still ran into a problem with extracting the splits.
def qifreadblock():
keys = []
values = []
v = (0000,"",0.,"","","N")
(date,num,amt,payee,memo,cleared) = v # now at least everything we want to return is initialized
while 1:
line = qif.readline() # gulp on a line of the file with handle 'qif'
if line == "^
" or line == "
" :
break # don't want to chew on more than one transaction at a time
elif line == "" :
raise IOError('EOF') # tell the main calling program that we've reached the end of the file
else :
keys.append(line[0])
values.append(re.sub('
','',line[1:])) # break the current line in two and append to the lists
while "D" in keys : # extract the date and shorten the two lists
date = values[keys.index("D")]
del values[keys.index("D")]
keys.remove("D")
while "N" in keys : # extract the reference number and shorten the two lists
num = values[keys.index("N")]
del values[keys.index("N")]
keys.remove("N")
while "T" in keys : # extract the transaction amount and shorten the two lists
amt = values[keys.index("T")]
del values[keys.index("T")]
keys.remove("T")
while "L" in keys : # extract the third party name and shorten the two lists
payee = values[keys.index("L")]
del values[keys.index("L")]
keys.remove("L")
while "P" in keys : # extract the memo line and shorten the two lists
memo = values[keys.index("P")]
del values[keys.index("P")]
keys.remove("P")
while "C" in keys : # extract the cleared status and shorten the two lists
cleared = re.sub(r'*','Y',values[keys.index("C")])
del values[keys.index("C")]
keys.remove("C")
splits = []
while "S" in keys: # go through every explicitly-named split and find out where the money went
Iacct = keys.index("S")
Iamt = keys.index("$")
try :
Imemo = keys.index("E")
Smemo = values[Imemo]
del values[Imemo]
except ValueError : Smemo = '' # it's not often a split will have its own memo, you know.
acctree = values[Iacct].split(":") # taking apart the branches of the account hierarchy
Sacct = acctree[-1] # only the last branch is relevant to Quickbooks
sli = [Sacct,values[Iamt],Smemo]
del values[Iacct]
del values[Iamt]
del keys[Iacct]
del keys[Iamt]
splits.append(sli) # add the just-defined split to the master list for this transaction
if len(splits) == 0: # even if no splits were explicitly defined, the IIF file still needs one.
sli = [payee,-float(amt),'']
splits = [sli]
# debug time !
print splits
return [date,num,amt,payee,memo,cleared,splits]
I know the error lies somewhere in this function, because the printout of the list of splits gives me
converting block 1 ...
[['Unnamed Employee', '-512.50', ''], ['Federal Payroll Tax Payable',
'-512.50', ''], ['State Payroll Tax Payable', '39.20', '']]
which is clearly wrong. I think I’m misunderstanding something fundamental about how the ‘while’ code block is executed. Are all the enclosed commands attempted before the condition is tested again? Or is the condition tested after each command? If the latter, I would appreciate some helpful suggestions for refactoring the code so that it does what I want.