#!/usr/bin/env python # -*- encoding:utf8 -*- # ########################################################################### # Copyright (C) 2006 - Håvard Dahle # # # Lisens: GPL2 # # $Id: 0.3 $ # # Dette skriptet konverterer fra kontoutskrifter lastet ned fra Skandiabanken # til det mer generelle, de facto utvekslingsformatet QIF. QIF kan importeres # til KMyMoney, GnuCash, Microsoft Money og Quicken. # # QIF filformat: # QIF er et rimelig skjørt og udokumentert filformat. Dette skriptet lager # QIF-filer som fungerer fint med KMyMoney, men det kan feile i andre program. # # Mer om QIF: # http://www.respmech.com/mym2qifw/qif_new.htm # # ########################################################################### import sys, os.path, re, md5, time from StringIO import StringIO class qifskriver: transaksjonstyper = {'E':[], 'I':[]} filkart = {} def __init__ (self, frafiler): self.filer = frafiler for f in frafiler: konto, aar = self._analyser_filnavn(f) if not self.filkart.has_key(konto): self.filkart[konto] = {} if not self.filkart[konto].has_key(aar): self.filkart[konto][aar] = {'buf':None, 'inn':[]} self.filkart[konto][aar]['inn'].append(f) def konverter(self, tilfil=None): if tilfil is None: til = sys.stdout else: til = open(tilfil, "w") for konto in self.filkart.keys(): for aar in self.filkart[konto].keys(): self.filkart[konto][aar]['buf'] = StringIO() for f in self.filkart[konto][aar]['inn']: self._konv(f, self.filkart[konto][aar]['buf']) til.write(self._list_kategorier()) for konto in self.filkart.keys(): for aar in self.filkart[konto].keys(): self.filkart[konto][aar]['buf'].seek(0) til.write("""!Type:Bank D%s-01-01 POpening Balance T0.00 CX L[konto%s] ^ """ % (aar, konto)) til.write(self.filkart[konto][aar]['buf'].read()) def _list_kategorier(self): s = "!Type:Cat\n" for innkat in self.transaksjonstyper['I']: s += "N%s\nI\n^\n" % innkat for utkat in self.transaksjonstyper['E']: s += "N%s\nE\n^\n" % utkat return s def konverter_ny(self): base, etter = os.path.splitext(self.filnavn) nyfil = "/tmp/" + base + ".qif" return self.konverter(nyfil) def konverter_fil(self, tilfil=None): if tilfil is None: til = sys.stdout #else: #til = open(tilfil, "w") self._konv(til) def _analyser_transaksjon(self, tr, inntekt): if inntekt: y = "I" else: y = "E" if self.transaksjonstyper[y].count(tr): return self.transaksjonstyper[y].append(tr) def _analyser_skilletegn(self, linje): #BOKF�INGSDATO";"RENTEDATO";"BRUKSDATO";"ARKIVREFERANSE";"TYPE";"TEKST";"UT FRA KONTO";"INN P�KON for z in ('\t', ';', ','): if len(linje.split(z)) == 8: return z raise "Kan ikke finne skilletegn" def _analyser_filnavn(self, filnavn): #97101163680_2004_nov.CSV filnavn = os.path.basename(filnavn) base, etter = os.path.splitext(filnavn) mnder = ['jan', 'feb', 'mar', 'apr', 'mai', 'jun', 'jul', 'aug', 'sep', 'okt', 'nov', 'des'] if base[0:11].isdigit(): kontonr = base[0:11] u_aar = base[12:16] #u_mnd = base[17:20] #utskriftsdato = "%s-%s-01" % (u_aar, mnder.index(u_mnd) + 1) #return utskriftsdato, kontonr return kontonr, u_aar else: #return "2001-01-01", "konto" return "xxxxxxxxxxxx", "2001-01-01" def _konv(self, innfil, utfil, modus='csv'): inntegnsett = "latin1" uttegnsett = "utf8" f = file(innfil) topp = f.readline() skilletegn = self._analyser_skilletegn(topp) for linje in f: #BOKF�INGSDATO";"RENTEDATO";"BRUKSDATO";"ARKIVREFERANSE";"TYPE";"TEKST";"UT FRA KONTO";"INN P�KON #"2005-05-09";"2005-05-09";"09.05.2005";"93070628";"Overfrsel";"UTLBET; ID 9710022000266641";245,73; try: bokdato, rentedato, bruksdato, ref, _type, tekst, ut, inn = linje.decode(inntegnsett).encode(uttegnsett).split(skilletegn) except ValueError: raise linje #pass ## TODO NBNBN XXXX dato = self._strip(bokdato) kategori = self._strip(tekst) transaksjonstype = self._strip(_type) #if kategori[0:4] in ('FRA-','TIL-'): # betalingsmottaker / betaler finnes i teksten #kategori = kategori[5:] #bet = re.match(r'^(.*)BETNR-\ \d+$', kategori) #try: kategori = bet.group(1) #except AttributeError: pass betx = re.match(r'^([A-Z ]+)?(FRA|TIL)-\ (.+)(BETNR-\ \d+)?$', kategori) if betx: kategori = betx.group(3) try: kategori = re.match(r'(.+) BETNR-\ \d+$', kategori).group(1) except AttributeError: pass if transaksjonstype.lower() in ('overførsel', 'overføring'): # er betalingsoverførsel # se om det er verdig informasjon å bruke som betalingspart # finn ut retning på overføringen if ut: transaksjonstype += " ut" else: transaksjonstype += " inn" if 'mellom egne konti' in kategori.lower(): # egen overføring kategori = "Intern overføring" if transaksjonstype.lower().startswith("visa") and re.match(r'^\d{6}\ .*$', kategori): #VISAkortnummer finnes i teksten kortnr = kategori[0:6] ref = self._strip(ref) + " VISA/%s" % kortnr kategori = kategori[7:] if re.match(r'^\d\d\.\d\d', kategori): #bruksdato finnes i teksten dag = kategori[0:2] mnd = kategori[3:5] aar = dato[0:4] if mnd == "12" and bokdato.split("-")[1] == "01": # transaksjonen var forrige år aar = str(int(aar)-1) dato = "%s-%s-%s" % (aar, mnd, dag) kategori = kategori[5:].strip() if transaksjonstype.lower().startswith("visa") and not ut: # penger er satt inn på visakontoen transaksjonstype += " innskudd" valx = re.match(r'^([A-Z]{3})\ (\d+,\d\d\) (.*)$', kategori) #Valutainfo finnes i teksten if valx: transaksjonstype += ":" + valx.group(1) #hva skal vi gjøre med valutabeløpet? #valutamengde = valx.group(2) kategori = valx.group(3) if transaksjonstype.lower().startswith("visa") and kategori[0:2] in ('S*', 'SÆ', 'M*', 'MÆ'): # visa-transaksjonen er varesalg (hva betyr 'Mx'?) transaksjonstype = transaksjonstype + "/" + kategori[0:1] kategori = kategori[2:] if re.match(r'^\d{4}\.\d{2}\.\d{5}$', kategori): #teksten er bare et kontonummer kategori = "FRA KONTONUMMER " + kategori # siste test: har noen av feltene blitt tomme? if len(kategori.strip()) == 0: kategori = "Ukjent" if ut: belop = "(%s)" % self._penger(ut) else: belop = self._penger(inn.strip()) utfil.write("""D%s T%s P%s N%s M%s L%s #%s ^ """ %(dato, belop, kategori, self._strip(ref), self._strip(tekst), transaksjonstype, self._id(dato, tekst))) #f.close() self._analyser_transaksjon(transaksjonstype, inntekt=bool(inn.strip())) def _id(self, dato, tekst): # lag en unik id _id = ''.join([str(ord(z)) for z in tekst.replace(" ", "")]) return "%s-%s-%s" % (dato, _id, time.time()) def _strip(self, s): s = s.strip() if s[0] in ('"', "'"): s = s[1:] if s[-1] in ('"', "'"): s = s[:-1] if not s: return "" if s[0] == "*": try: s = s[1:] except IndexError: return "" s = s.replace(":", "-") return s def _penger(self, p): # bytt til engelsk desimaltegn etc mx = re.match(r'^(\d+),(\d\d)$', p) try: p = "%s.%s" % (mx.group(1), mx.group(2)) except AttributeError: pass return p if __name__ == '__main__': konv = qifskriver(sys.argv[1:]) #konv.konverter() konv.konverter()