Welcome To Our Shell

Mister Spy & Souheyl Bypass Shell

Current Path : /home/ift/52_procpy/fibu/

Linux ift1.ift-informatik.de 5.4.0-216-generic #236-Ubuntu SMP Fri Apr 11 19:53:21 UTC 2025 x86_64
Upload File :
Current File : //home/ift/52_procpy/fibu/account5.py

#  coding:  utf8

import os,sys,re,glob,time,sqlite3,hashlib,time

#******************************************************************************

#class Account5 (object):
#
#    def __init__ (self):
#        self.mark()
#
#    def read_kto (self):
#    
#        open("report.kto").read()


#******************************************************************************

class Kto (object):

    def __init__ (self,ktodir):
    
        self.ktodir = re.sub(r"[\\\/]+$","",ktodir)
        
        ktofile = self.find_ktofile()
        if ktofile:
            self.text    = open(ktofile).read()
        else:
            self.text    = ""
            
        self.db = None
        self.read_hash_keys_from_ktodir(ktofile)
        self.read_ukto_and_name_from_ktodir()
        self.read_monatsfilter()
        self.read_interval()
        
    def mark (self,remark=""):
    
        t = time.clock()
        if 't0' in vars(self):
            print ( ("%9.2f" % ((t-self.t0)*1000)) + " ms for:  " + remark )
        self.t0 = t

    def find_ktofile (self):
    
        ktofiles = glob.glob(self.ktodir+"/report_*.kto")
        if len(ktofiles) == 1:
            return ktofiles[0]
        elif len(ktofiles) == 0:
            return ""
        else:
            return None

    def read_hash_keys_from_ktodir (self,ktofile):
        
        m = re.search(r"^(.*)\_([a-z0-9]{8})\_([a-z0-9]{8})\.kto$",ktofile)
        if m:
            self.old_db  = m.group(2)
            self.old_key = m.group(3)
        else:
            self.old_db  = ""
            self.old_key = ""

    def read_ukto_and_name_from_ktodir (self):

        self.name = ""

        m = re.search(r"^(.*)[\\\/](.*)",self.ktodir)
        if m:
            self.ukto = m.group(2)
        else:
            self.ukto = ""
            
        m = re.search(r"^(.*?)\_\_(.*)$",self.ukto)
        if m:
            self.name = m.group(2)
            self.ukto = m.group(1)
        if not self.ukto == "":
            self.ukto = self.ukto + "-"
            
    def read_monatsfilter (self):
    
        m = re.search(r"^(.*)([123456789IJKLMNPS])\-$",self.ukto)
        if m:
            self.ukto            = m.group(1)
            self.with_startsaldo = False
            self.monatsfilter    = { 
               "1" : ["01"],
               "2" : ["02"],
               "3" : ["03"],
               "4" : ["04"],
               "5" : ["05"],
               "6" : ["06"],
               "7" : ["07"],
               "8" : ["08"],
               "9" : ["09"],
               "A" : ["10"],
               "B" : ["11"],
               "C" : ["12"],
               "I" : ["01","02","03"],
               "J" : ["04","05","06"],
               "K" : ["07","08","09"],
               "L" : ["10","11","12"],
               "M" : ["01","02","03","04","05","06"],
               "N" : ["07","08","09","10","11","12"],
               "P" : ["01","02","03","04","05","06","07","08","09","10","11","12"] } [ m.group(2) ]
        else:
            self.with_startsaldo = True
            self.monatsfilter    = ["01","02","03","04","05","06","07","08","09","10","11","12"]
            
    def read_interval (self):
    
        self.startdatum = "00000000"
        self.enddatum   = "99999999"
        
        m = re.search(r"^(.*\-)(\d\d\d\d)\_(\d\d\d\d)-$",self.ukto)
        if m:
            self.ukto       = m.group(1)
            self.startdatum = ( m.group(1) + "0000" )[0:8]
            self.enddatum   = ( m.group(2) + "9999" )[0:8]
            return

        m = re.search(r"^(.*\-)(20\d\d)-$",self.ukto)
        if m:
            self.ukto       = m.group(1)
            self.startdatum = m.group(2) + self.monatsfilter[0]  + "00"
            self.enddatum   = m.group(2) + self.monatsfilter[-1] + "99"
            return
            
        if re.search(r"^(.*\-)(20\d\d)(01|02|03|04|05|06|07|08|09|10|11|12)-$",self.ukto):
            self.ukto       = m.group(1)
            self.startdatum = m.group(2) + m.group(3) + "00"
            self.enddatum   = m.group(2) + m.group(3) + "99"
            return
            
    def reset (self,parent):

        self.parent   = parent
        self.new_key  = self.compute_ktokey()
        self.new_db   = self.compute_dbkey(parent.db)
        if self.old_key == "":
            self.old_db = self.new_db

    def sync_unterkonten (self):
    
        self.connect_to_db()
        kontenliste = []
        for entry in glob.glob(self.ktodir+"/*"):
            if os.path.isdir(entry):
                unterkonto = Kto(entry)
                unterkonto.reset(self)
                kontenliste.append( unterkonto )

        while (0 == 0):
            konten_have_changed = False
            for konto in kontenliste:
#                print (konto)
                change_of_kto = konto.fit_to_parent(self.db)
                if change_of_kto:
                    konten_have_changed = True
            if not konten_have_changed:
                break
        self.db.commit()
        
    def show_keys (self):

        ktofile = self.find_ktofile()
        anzeige = ("%-25s" % (ktofile+","))
        anzeige = anzeige + " KEY: " + self.new_key + " / " + ("%-8s" % self.old_key)
        anzeige = anzeige + ", PARENT: " + self.old_db + " / " + ("%-8s" % self.new_db)
        print (anzeige)

    def fit_to_parent (self,db):   #   State machine

        self.new_db  = self.compute_dbkey(db)
        self.new_key = self.compute_ktokey()

        self.show_keys()

        if not self.new_key == self.old_key and self.new_db == self.old_db:
            print (124)
            self.write_to_parent()
            self.new_key = self.compute_ktokey()
            self.old_key = self.new_key
            self.parent.mark( ("%-20s"%(self.ukto[:-1]+",")) + "Buchungen imported to parent.")
            return True
            
        if self.new_key == self.old_key and not self.new_db == self.old_db:
            print (125)
            adjusted = self.get_from_parent()
            self.write_new_ktofile()
            self.new_key = self.compute_ktokey()
            self.old_db = self.new_db
            if not adjusted:
                print("MARKADD")
                self.add_to_ktofile("\n---\n")
            self.parent.mark( ("%-20s"%(self.ukto[:-1]+",")) + "Buchungen refreshed from parent. Parent key: " + self.old_db)
            return True

        return self.apply_rule()

    def apply_rule (self):
    
        self.description_of_recent_action = "Rule applied."
        return False

    def write_to_parent (self):
    
        buchungs_ids = []
        cursor       = self.parent.db.cursor()
        self.delete_buchungen_from_parent(cursor)

        for zeile in self.text.split("\n"):
            self.insert_buchung_to_parent(cursor,zeile,buchungs_ids)
            
        ktofile     = self.find_ktofile()
        ktofile_new = self.ktodir + "/report_"+self.new_db+"_"+self.new_key+".kto"
        os.rename(ktofile,ktofile_new)
        return True
    
    def delete_buchungen_from_parent (self,cursor):

        cursor.execute("delete from buchungen where (KTOA like '" +    
                   self.ukto + "%' OR " + " KTOB like '" + self.ukto + "%') " +
                   " and DATUM >= '" + self.startdatum + "' and DATUM <= '" + self.enddatum + "' ")

    def insert_buchung_to_parent (self,cursor,zeile,buchungs_ids):

        m = re.search(r"^(\d\d\d\d\d\d\d\d) +(\S+) +(\S*) +(\S+) +(\-?\d+\.\d\d) +(.*)$",zeile)
        if not m:
            return
    
        datum  = m.group(1)
        betrag = "%3.2f" % eval(m.group(2))
        ktoa   = self.compute_db_ktoname(m.group(3))
        ktob   = self.compute_db_ktoname(m.group(4))
        remark = m.group(6)

        if ktoa > ktob:
            ktoa, ktob, betrag = self.switch_ktoa_ktob(ktoa,ktob,betrag)
        buchungs_id        = self.compute_buchungs_id(ktoa,ktob,betrag,remark,datum)
        if not buchungs_id in buchungs_ids:
            buchungs_ids.append(buchungs_id)
            qstr = ("insert into buchungen (DATUM,BETRAG,KTOA,KTOB,REMARK) values ('" + datum + "'," +
                     betrag + ",'" + ktoa + "','" + ktob + "','" + re.sub(r"'","",remark,9999) + "')")
            cursor.execute(qstr)

    def compute_buchungs_id (self,ktoa,ktob,betrag,remark,datum):
    
        buchungs_id = ",".join([ktoa,ktob,betrag,remark,datum])
        return buchungs_id

    def switch_ktoa_ktob (self,ktoa,ktob,betrag):

        o      = ktoa  #  Hin- und Herschwingen zu Endlos-Loops kommen
        ktoa   = ktob
        ktob   = o
        betrag = "%3.2f" % (- float(betrag))
        return ktoa, ktob, betrag

    def compute_ukto_ktoname (self,ktox):
    
        ktoy = re.sub(self.ukto,"",ktox)
        if ktoy == ktox:
            return(ktoy[:-1])
        else:
            return("-"+ktoy[:-1])

    def compute_db_ktoname (self,ktox):
    
        if ktox[0] == "-":
            ktox = self.ukto + ktox[1:] + "-"
        return ktox

    def extract_to_ukto (self,ukto):

        self.connect_to_db()
        new_ukto  = Kto(self.ktodir+"/"+ukto)
        new_ukto.reset(self)
        
        while (0 == 0):
            konto_has_changed = new_ukto.fit_to_parent(self.db)
            if not konto_has_changed:
                break
        self.db.commit()

    def get_from_parent (self):
    
        cursor = self.parent.db.cursor()
        qstr   =        " select DATUM,BETRAG,KTOA,KTOB,REMARK from buchungen "
        qstr   = qstr + " where (KTOA like '" + self.ukto + "%' or KTOB like '" + self.ukto + "%')"
        qstr   = qstr + " and DATUM >= '" + self.startdatum + "' and DATUM <= '" + self.enddatum + "' "
        qstr   = qstr + " order by DATUM,BETRAG,KTOA,KTOB" 
#        print (qstr)
        cursor.execute(qstr)
        
        try:
            maxa_old         = self.maxa
            maxb_old         = self.maxb
        except:
            maxa_old         = 10
            maxb_old         = 10

        self.format_maxa = "%-" + str(maxa_old) + "s"
        self.format_maxb = "%-" + str(maxb_old) + "s"

        print ( self.format_maxa, self.format_maxb )

        self.maxa = 0
        self.maxb = 0

        self.salden = {}
        text_intern, text_extern = self.compute_intern_and_extern_buchungen(cursor)
        text_salden              = self.compute_salden()

        self.kontotext = []
        gesamt        = 0.00

        for buchung in text_extern:

            betrag = buchung[1]
            gesamt = "%13.2f" % ( float(gesamt) + float(betrag) )
            buchungszeile = self.format_buchung(buchung,gesamt)
            self.kontotext.append(buchungszeile)

        self.kontotext.append("")

        for saldo in text_salden:
            
            self.kontotext.append(saldo)

        self.kontotext.append("")

        for buchung in text_intern:

            buchungszeile = self.format_buchung(buchung,"        0.00")
            self.kontotext.append(buchungszeile)

        if maxa_old == self.maxa and maxb_old == self.maxb:
            return True
        else:
            return False

    def write_new_ktofile (self):

        ktofile = self.find_ktofile()
        if ktofile:
            os.unlink(ktofile)
        ktofile_new = self.ktodir + "/report_"+self.new_db+"_"+self.new_key+".kto"

        with open(ktofile_new,"w") as ktofilehandle:
            ktofilehandle.write("\n".join(self.kontotext))

    def add_to_ktofile (self,addtext):

        ktofile = self.find_ktofile()
        if ktofile:
            with open(ktofile,"a") as ktofilehandle:
                ktofilehandle.write(addtext)


    def format_buchung (self,buchung,gesamt):

         zeile = buchung[0] + " " + buchung[1] + "  " + buchung[2] + "  " +buchung[3] + " " + gesamt + "  " + buchung[4]
         return zeile


    def compute_intern_and_extern_buchungen (self,cursor):

        text_intern      = []
        text_extern      = []
        

        while (0 == 0):
            entry = cursor.fetchone()
            if not entry:
                break

            buchung, anzahl_der_internen_konten = self.compile_buchung(entry)
            if anzahl_der_internen_konten == 1:
                text_extern.append(buchung)
            else:
                text_intern.append(buchung)

        text_intern.sort(key=lambda x:x[5])
        text_extern.sort(key=lambda x:x[5])
        
        return text_intern, text_extern


    def compile_buchung (self,entry):

        datum   = entry[0]
        betrag  = float(entry[1])
        ktoa    = self.compute_ukto_ktoname(entry[2])
        ktob    = self.compute_ukto_ktoname(entry[3])
        if re.search(r"^\d\d? +v\.?H\.? ",entry[4]):   #  USt-Einrueckung
            remark = "   " + entry[4]
        else:
            remark = entry[4]
            
        if self.kto_is_extern(ktoa) and self.kto_is_intern(ktob):
            ktoa, ktob, betrag = self.switch_ktoa_ktob(ktoa,ktob,betrag)

        anzahl_der_internen_konten = 0
        if self.kto_is_intern(ktoa):
            self.salden_update(ktoa, float(betrag))
            anzahl_der_internen_konten = anzahl_der_internen_konten + 1
        if self.kto_is_intern(ktob):
            self.salden_update(ktob,-float(betrag))
            anzahl_der_internen_konten = anzahl_der_internen_konten + 1
                
        self.maxa = max(len(ktoa),self.maxa)
        self.maxb = max(len(ktoa),self.maxb)

        sortidx = datum
        buchung = [  
                     datum,
                     ("%13.2f" % betrag),
                     (self.format_maxa % ktoa),
                     (self.format_maxb % ktob),
                     remark,
                     sortidx
                  ]
           
        return buchung, anzahl_der_internen_konten

    def compute_salden (self):

        text_salden = []
        saldenliste = list(self.salden.keys())
        saldenliste.sort()
        
        for unterkonto in saldenliste:
            einrueckung = "    " * len(re.sub(r"[^\-]","",unterkonto,9999))
            text_salden.append(("%-35s" % unterkonto[1:]) + einrueckung + ("%13.2f" % self.salden[unterkonto]))
            
        return(text_salden)

    def salden_update (self,ktox,betrag):
    
        if ktox == "-":
            ktox = ""
            
        if ktox in self.salden:
            self.salden[ktox] = self.salden[ktox] + betrag
        else:
            self.salden[ktox] = betrag
            
        m = re.search(r"^(.*)\-(.*)$",ktox)
        if m:
            self.salden_update(m.group(1),betrag)
            
    def xxformat_kto (self):
    
        text_intern  = []
        text_extern  = []
        
        while (0 == 0):    #  Zeilen des Kontos schreiben
           
            if len(saldovortrag) > 0:
                entry        = [ ktodata['START'], saldovortrag[0], ktodata['UKTO1'], "-11-1805", "Saldovortrag"]
                saldovortrag = []
            else:
                entry = cursor.fetchone()
                if not entry:
                    break

            if t == "":
                if not "Saldovortrag" in entry[4] :
                    t = "___CONTAINS___BUCHUNGEN___"
                    
            betrag    = float(entry[1])
            ktoa      = entry[2]
            ktob      = entry[3]

            anz       = 0
            if ktoa[0:ul] == ukto1:
                ktoa = ktoa[ul:]
#                self.compute_salden(ktoa,betrag)
                if ktoa == "":
                    ktoa = "-"
                anz = anz + 1
            else:
                ktoa = ktoa[1:]
            if ktob[0:ul] == ukto1:
                ktob = ktob[ul:]
#                self.compute_salden(ktob,-betrag)
                if ktob == "":
                    ktob = "-"
                if anz == 0:
                    o      = ktoa
                    ktoa   = ktob
                    ktob   = o
                    betrag = -betrag
                anz = anz + 1
            else:

                ktob = ktob[1:]
         
            datum   = entry[0]
            
#            if datum == datum0 and betrag == betrag0 and abs(betrag) > 0.001:
#                print ("WARNING:",datum,("%13.2f"%betrag))
            datum0  = datum
            betrag0 = betrag
            
            remark  = entry[4]

            sortidx = datum
            m       = re.search(r"^(.*)? *\{\{SORTIDX\: +(.*?) *\}\} *(.*)$",remark)  #   proprietaeren Sort-Index herauslesen
            if m:
                remark  = (m.group(1) + " " + m.group(3)).strip()
                sortidx = sortidx + (m.group(2) + " "*40)[0:40]

            sortidx = sortidx + self.rules.sortidx(ktoa)
            m = re.search(r"^(\d\d? v\.?H\.? +.*\()(.*)\)",remark)
            if m:
                sortidx = sortidx + (m.group(2)+" "*40)[0:40] + "-Z"
            else:
                sortidx = sortidx + (re.sub(r"^(\+\-|\+\+)","",remark) + " "*40)[0:40] + "-A"

            sortidx = sortidx + ktob
            ktoa    = re.sub("^"+ukto1,"",ktoa)

            if ktoa == "":
                ktoa = "-"
                
            maxa    = max(maxa,len(ktoa))
            maxb    = max(maxb,len(ktob))
            buchung = datum +  " " + ("%13.2f" % betrag) + "  " + (maxa0 % ktoa)  + "  " +  (maxb0 % ktob) + "  "
            sp      = ""
            if re.search(r"^\d\d? +v\.?H\.? ",entry[4]):   #  USt-Einrueckung
                sp = "   "
            if anz == 1:
                text_extern.append([buchung,"  " + sp + remark,betrag,sortidx])
            else:
                text_intern.append([buchung + "         0.00  " + sp + remark,"","",sortidx])
            
        text_intern.sort(key=lambda x: x[3])
        text_extern.sort(key=lambda x: x[3])
        
#        self.mark("Start Salden")

        fac = 1
        for kto in ("KTOA","KTOB"):
            qstr   =        " select " + kto + ",sum(BETRAG) from buchungen "
            qstr   = qstr + " where " + kto + " like '" + ktodata['UKTO1'] + "%'"
            qstr   = qstr + " and DATUM >= '" + ktodata['START'] + "' and DATUM <= '" + ktodata['ENDE'] + "' "
            qstr   = qstr + " group by (" + kto + ")" 
            cursor.execute(qstr)
            while (0 == 0):
                entry = cursor.fetchone()
                if not entry:
                    break
                ukto   = entry[0][ul:]
                betrag = float(entry[1])
                while (0 == 0):
                    if not ukto in salden:
                        salden[ukto] = 0.00000001
                    salden[ukto] = salden[ukto] + fac * betrag
                    m = re.search(r"^(.*)\-(.*)$",ukto)
                    if m:
                        ukto = m.group(1)
                    else:
                        break
            fac = -1
#        self.mark("Ende Salden")

#        print (salden)

        ktoliste = []      #  Kontenliste sortieren
        for kto in salden.keys():
            ktoliste.append([kto,self.rules.sortidx(re.sub(r"^-","",kto))])
        ktoliste.sort(key=lambda x: x[1])
        konten = []

        text = []
        
        gesamt = 0.00
        for o in text_extern:
            gesamt = gesamt + float(o[2])
            text.append(o[0]+("%13.2f" % gesamt)+o[1])
        text.append("")

        for kto0 in ktoliste:
            kto  = kto0[0]
            kto2 = re.sub(r"^\-","",kto)
            sp   = re.sub(r"[^\-]","",kto)
            name = self.get_ktoname(ktodata['UKTO1']+kto,ktodata['START'],ktodata['ENDE'])
            if self.rules.empty_line_in_ktolist(kto2):
                text.append("")
            text.append( ("%-56s" % (("%-20s" % kto2) + name ) ) +
                                re.sub(r"\-","    ",sp,9999) + ("%13.2f" % salden[kto]) )
            if not "-" in kto2:
                if not name == "":
                    name = "__" + name
                if not 'UKTODIRS' in ktodata:
                    ktodata['UKTODIRS'] = {}
                ktodata['UKTODIRS'][kto2+name] =1
        text.append("")

        
        for o in text_intern:
            text.append(o[0])
        text.append("")

        if not ktodata['INT'] == "":
            o = "-" + ktodata['INT']
        else:
            o = ""
        
        text1 = ktodata['HEADER']
        text1 = text1 + ("%-30s" % (ktodata['UKTO'] + o)) + "  "
        text2 = "  "  + ktodata['INTERVAL'] + "  " + ("%13.2f" % float(gesamt) ) + "\n\n"
        text2 = text2 + "\n".join(text) + "\n"

        ktodata['NEWHASH'] = self.compute_ktohash(ktodata,t+text1+text2+ktodata['ADDTEXT'])
#        if ktodata['NEWHASH'] == "xxxxxx":
#            ktodata['NEWHASH'] = ktodata['KTOHASH']
#        print ("----->",123)        


        ktodata['CONTENT'] = text1 + "(" + ktodata['DBHASH'] + ktodata['NEWHASH'] + ")" + text2

        if not ktodata['MAXA'] == maxa or not ktodata['MAXB'] == maxb:
            ktodata['MAXA']    = maxa
            ktodata['MAXB']    = maxb
            ktodata['CONTENT'] = ktodata['CONTENT'] + " "  # damit der KTOHASH nicht mehr stimmt und neu berechnet wird

            
    def kto_is_intern (self,ktox):
    
        if ktox[0] == "-" or len(ktox) == 0:
            return True
        else:
            return False
            
    def kto_is_extern (self,ktox):
    
        if ktox[0] == "-" or len(ktox) == 0:
            return False
        else:
            return True
            
    def xxx1 (self):

        self.maxa0

        saldovortrag = []
        salden       = {}

        if ktodata["INT"] == "":
            saldovortrag = [0.0]            
            for kto in ("KTOA","KTOB"):
                cursor = self.db.cursor()
                qstr   =        " select sum(BETRAG) from buchungen "
                qstr   = qstr + " where (" + kto + " like '" + ktodata['UKTO1'] + "%')"
                qstr   = qstr + " and DATUM < '" + ktodata['START'] + "' "
                cursor.execute(qstr)
                o = cursor.fetchone()[0]
                if o:
                    saldovortrag[0] = saldovortrag[0] + float(o)
                saldovortrag[0] = -saldovortrag[0]
            salden[""] = saldovortrag[0]


    def xxx ():

        
        ukto1        = ktodata['UKTO1']
        ul           = len(ukto1)
        gesamt       = 0.00
        maxa         = 9
        maxb         = 9
        maxa0        = "%-" + str(ktodata['MAXA']) + "s"
        maxb0        = "%-" + str(ktodata['MAXB']) + "s"
        text_intern  = []
        text_extern  = []
        t            = ""
        datum0       = ""
        betrag0      = ""
        
        while (0 == 0):    #  Zeilen des Kontos schreiben
           
            if len(saldovortrag) > 0:
                entry        = [ ktodata['START'], saldovortrag[0], ktodata['UKTO1'], "-11-1805", "Saldovortrag"]
                saldovortrag = []
            else:
                entry = cursor.fetchone()
                if not entry:
                    break

            if t == "":
                if not "Saldovortrag" in entry[4] :
                    t = "___CONTAINS___BUCHUNGEN___"
                    
            betrag    = float(entry[1])
            ktoa      = entry[2]
            ktob      = entry[3]

            anz       = 0
            if ktoa[0:ul] == ukto1:
                ktoa = ktoa[ul:]
#                self.compute_salden(ktoa,betrag)
                if ktoa == "":
                    ktoa = "-"
                anz = anz + 1
            else:
                ktoa = ktoa[1:]
            if ktob[0:ul] == ukto1:
                ktob = ktob[ul:]
#                self.compute_salden(ktob,-betrag)
                if ktob == "":
                    ktob = "-"
                if anz == 0:
                    o      = ktoa
                    ktoa   = ktob
                    ktob   = o
                    betrag = -betrag
                anz = anz + 1
            else:
                ktob = ktob[1:]
         
            datum   = entry[0]
            
#            if datum == datum0 and betrag == betrag0 and abs(betrag) > 0.001:
#                print ("WARNING:",datum,("%13.2f"%betrag))
            datum0  = datum
            betrag0 = betrag
            
            remark  = entry[4]

            sortidx = datum
            m       = re.search(r"^(.*)? *\{\{SORTIDX\: +(.*?) *\}\} *(.*)$",remark)  #   proprietaeren Sort-Index herauslesen
            if m:
                remark  = (m.group(1) + " " + m.group(3)).strip()
                sortidx = sortidx + (m.group(2) + " "*40)[0:40]

            sortidx = sortidx + self.rules.sortidx(ktoa)
            m = re.search(r"^(\d\d? v\.?H\.? +.*\()(.*)\)",remark)
            if m:
                sortidx = sortidx + (m.group(2)+" "*40)[0:40] + "-Z"
            else:
                sortidx = sortidx + (re.sub(r"^(\+\-|\+\+)","",remark) + " "*40)[0:40] + "-A"

            sortidx = sortidx + ktob
            ktoa    = re.sub("^"+ukto1,"",ktoa)

            if ktoa == "":
                ktoa = "-"
                
            maxa    = max(maxa,len(ktoa))
            maxb    = max(maxb,len(ktob))
            buchung = datum +  " " + ("%13.2f" % betrag) + "  " + (maxa0 % ktoa)  + "  " +  (maxb0 % ktob) + "  "
            sp      = ""
            if re.search(r"^\d\d? +v\.?H\.? ",entry[4]):   #  USt-Einrueckung
                sp = "   "
            if anz == 1:
                text_extern.append([buchung,"  " + sp + remark,betrag,sortidx])
            else:
                text_intern.append([buchung + "         0.00  " + sp + remark,"","",sortidx])
            
        text_intern.sort(key=lambda x: x[3])
        text_extern.sort(key=lambda x: x[3])
        
#        self.mark("Start Salden")

        fac = 1
        for kto in ("KTOA","KTOB"):
            qstr   =        " select " + kto + ",sum(BETRAG) from buchungen "
            qstr   = qstr + " where " + kto + " like '" + ktodata['UKTO1'] + "%'"
            qstr   = qstr + " and DATUM >= '" + ktodata['START'] + "' and DATUM <= '" + ktodata['ENDE'] + "' "
            qstr   = qstr + " group by (" + kto + ")" 
            cursor.execute(qstr)
            while (0 == 0):
                entry = cursor.fetchone()
                if not entry:
                    break
                ukto   = entry[0][ul:]
                betrag = float(entry[1])
                while (0 == 0):
                    if not ukto in salden:
                        salden[ukto] = 0.00000001
                    salden[ukto] = salden[ukto] + fac * betrag
                    m = re.search(r"^(.*)\-(.*)$",ukto)
                    if m:
                        ukto = m.group(1)
                    else:
                        break
            fac = -1
#        self.mark("Ende Salden")

#        print (salden)

        ktoliste = []      #  Kontenliste sortieren
        for kto in salden.keys():
            ktoliste.append([kto,self.rules.sortidx(re.sub(r"^-","",kto))])
        ktoliste.sort(key=lambda x: x[1])
        konten = []

        text = []
        
        gesamt = 0.00
        for o in text_extern:
            gesamt = gesamt + float(o[2])
            text.append(o[0]+("%13.2f" % gesamt)+o[1])
        text.append("")

        for kto0 in ktoliste:
            kto  = kto0[0]
            kto2 = re.sub(r"^\-","",kto)
            sp   = re.sub(r"[^\-]","",kto)
            name = self.get_ktoname(ktodata['UKTO1']+kto,ktodata['START'],ktodata['ENDE'])
            if self.rules.empty_line_in_ktolist(kto2):
                text.append("")
            text.append( ("%-56s" % (("%-20s" % kto2) + name ) ) +
                                re.sub(r"\-","    ",sp,9999) + ("%13.2f" % salden[kto]) )
            if not "-" in kto2:
                if not name == "":
                    name = "__" + name
                if not 'UKTODIRS' in ktodata:
                    ktodata['UKTODIRS'] = {}
                ktodata['UKTODIRS'][kto2+name] =1
        text.append("")

        
        for o in text_intern:
            text.append(o[0])
        text.append("")

        if not ktodata['INT'] == "":
            o = "-" + ktodata['INT']
        else:
            o = ""
        
        text1 = ktodata['HEADER']
        text1 = text1 + ("%-20s" % (ktodata['UKTO'] + o)) + "  "
        text2 = "  "  + ktodata['INTERVAL'] + "  " + ("%13.2f" % float(gesamt) ) + "\n\n"
        text2 = text2 + "\n".join(text) + "\n"

        ktodata['NEWHASH'] = self.compute_ktohash(ktodata,t+text1+text2+ktodata['ADDTEXT'])
#        if ktodata['NEWHASH'] == "xxxxxx":
#            ktodata['NEWHASH'] = ktodata['KTOHASH']
#        print ("----->",123)        


        ktodata['CONTENT'] = text1 + "(" + ktodata['DBHASH'] + ktodata['NEWHASH'] + ")" + text2

        if not ktodata['MAXA'] == maxa or not ktodata['MAXB'] == maxb:
            ktodata['MAXA']    = maxa
            ktodata['MAXB']    = maxb
            ktodata['CONTENT'] = ktodata['CONTENT'] + " "  # damit der KTOHASH nicht mehr stimmt und neu berechnet wird

        if self.old_key == self.old_key:
            return(False)
            
        if parentkey == self.parentkey:

            db.cursor().execute("delete from buchungen where (KTOA like '" +    
                       self.ukto + "%' OR " + " KTOB like '" + self.ukto + "%') " +
                       " and DATUM >= '" + self.startdatum + "' and DATUM <= '" + self.enddatum + "' ")

            unique_strings = {}
            ul             = len(self.ukto)
            for zeile in self.text.split("\n"):
                m = re.search(r"^(\d\d\d\d\d\d\d\d) +(\S+) +(\S*) +(\S+) +(\-?\d+\.\d\d) +(.*)$",zeile)
                if not m:
                    return
                datum  = m.group(1)
                betrag = "%3.2f" % eval(m.group(2))
                remark = m.group(6)
                uniqu  = []

                ktoa   = "-" + m.group(3)
                if ktoa == "--":
                    ktoa  = self.ukto
                elif ktoa[0:2] == "--":
                    ktoa  = self.ukto + ktoa[1:]
                elif not ktoa[0:ul] == self.ukto:
                    uniqu.append(ktoa)

                ktob   = "-" + m.group(3)
                if ktob == "--":
                    ktob  = self.ukto
                elif ktob[0:2] == "--":
                    ktob  = self.ukto + ktob[1:]
                elif not ktob[0:ul] == self.ukto:
                    uniqu.append(ktob)

                if ktoa > ktob:    #  um Eindeutigkeit zu schaffen. Sonst kann es durch
                    o      = ktoa  #  Hin- und Herschwingen zu Endlos-Loops kommen
                    ktoa   = ktob
                    ktob   = o
                    betrag = re.sub(r"^--","","-"+betrag)
                if uniqu == []:
                    uniqu = [ktoa,ktob]
                uniqu1 = []
                for k in uniqu:
                    uniqu1.append(re.sub(r"\-\d\d\d\d\d$","-BETRAG",k))

                betrag1 = re.sub("\-","",betrag) + ","
                remark1 = "," + re.sub(r"[\+\- ]","",remark,9999)
                uniqu   = betrag1 + ",".join(uniqu1) + remark1
                if datum in unique_strings:  #  to avoid double entries
                    if uniqu in unique_strings[datum]:
                        continue
                    else:
                        unique_strings[datum].append(uniqu)
                else:
                    unique_strings[datum] = [uniqu]  #  ,betrag1+"-13-9999"+remark1]
                    qstr = ("insert into buchungen (DATUM,BETRAG,KTOA,KTOB,REMARK,ID) values ('" + datum +
                            "'," + betrag + ",'" + ktoa + "','" + ktob + "','" +
                            re.sub(r"'","",remark,9999) + "','" + 'a' + "')")
                    db.cursor().execute(qstr)
                

    def compute_dbkey (self,db):
    
        qu_string = (" select DATUM,BETRAG,KTOB,REMARK " +
                     " from buchungen " +
                     " where (KTOA like '" + self.ukto + "%' or KTOB like '" + self.ukto + "%') " +
                     " and DATUM >= '" + self.startdatum + "' and DATUM <= '" + self.enddatum + "' " +
                     " order by DATUM,KTOA,KTOB,BETRAG,REMARK") 

        cursor = db.cursor()
        cursor.execute(qu_string)
        dbhash_new = [""]

        while (0 == 0):
            entry    = cursor.fetchone()
            if entry == None:
                break
            entry    = list(entry)
            entry[1] = "%3.2f" % entry[1]
            dbhash_new.append(re.sub(r" +"," "," ".join(list(entry)),99999999))
    
        db_key = hashlib.md5( "".join(dbhash_new).encode() ).hexdigest()[0:8]
        
        return db_key
        

    def connect_to_db (self):

        if not self.db:
        
            self.db             = sqlite3.connect(self.ktodir+"/report.sql")
            self.db.row_factory = sqlite3.Row
            cursor              = self.db.cursor()
            
            cursor.execute("create table if not exists buchungen " +
                           "(DATUM,BETRAG,KTOA,KTOB,REMARK default '')")

            try:
                cursor.execute("create index DATUM_IDX  on buchungen (DATUM)"   )
                cursor.execute("create index KTOA_IDX   on buchungen (KTOA)"    )
                cursor.execute("create index KTOB_IDX   on buchungen (KTOB)"    )
            except Exception as e:
                pass


    def sync_kto_to_sql (self,ktodir):
    
        self.db             = sqlite3.connect(dbfile)
        self.db.row_factory = sqlite3.Row
        

        for zeile in open(self.ktodir+"/report_*.kto").read():
            m = re.search(r"^(\d\d\d\d\d\d\d\d) +(\S+) +(\S*) +(\S+) +(\-?\d+\.\d\d) +(.*)$",zeile)
            if m:
                self.db             = sqlite3.connect(dbfile)
                self.db.row_factory = sqlite3.Row

        cursor = self.db.cursor()
        cursor.execute("create table if not exists buchungen " +
             "(DATUM,BETRAG,KTOA,KTOB,REMARK,ID default '')")
        cursor.execute("create table if not exists ktoname " +
             "(KTO,NAME,DATUMA,DATUMB)")

        try:
            cursor.execute("create index DATUM_IDX  on buchungen (DATUM)"   )
            cursor.execute("create index KTOA_IDX   on buchungen (KTOA)"    )
            cursor.execute("create index KTOB_IDX   on buchungen (KTOB)"    )
        except Exception as e:
            pass

        try:
            cursor.execute("create index KTO_IDX    on ktoname   (KTO)"     )
            cursor.execute("create index DATUMA     on ktoname   (DATUMA)"  )
        except Exception as e:
            pass
        
        if not len(ktofile) == 1:
            return("ERROR. No ktofile found or ambiguous in " + self.ktodir)

    def read_kto (self):  #  Parse the ktofile
        
        self.comment    = []
        self.buchungen  = []
        self.uktosalden = {}
        self.uktonames  = {}

        ktofile = glob.glob(self.ktodir+"/report_*.kto")
        if not len(ktofile) == 1:
            return("ERROR. No ktofile found or ambiguous in " + self.ktodir)
        self.ktofile = ktofile[0]
        
        self.read_hash_keys()
        self.compute_new_ktokey()
        
    def compute_ktokey (self):

        ktofiles = glob.glob(self.ktodir+"/*.*")
        ktofiles.sort()
        
        hashtext = []
        for file in ktofiles:
        
            if not os.path.isfile(file):
                continue
            if re.search(r"\.(jpg|pdf|xlsx?|docx?|sql|~)$",file):
                continue

#            print (file)
            filecontent = open(file).read()
            if re.search(r"\.kto$",file):
                filecontent = re.sub(r" ","",filecontent,99999999)
                filename    = "report"
            else:
                filename = re.sub(r"^(.*)[\\\/](.*)$","\\2",file)
            filehash = filename + "." + hashlib.md5( open(file).read().encode() ).hexdigest()
            hashtext.append(filehash)
        
        ktokey = hashlib.md5( "".join(hashtext).encode() ).hexdigest()[0:8]
        return ktokey

    def parse_zeile_for_comment (self,zeile):

        m = re.search(r"(  *)(\#.*)$",zeile)
        if m:
            self.comment.append(m.group(2))
            return True
        else:
            return False
        
    def parse_zeile_for_buchung (self,zeile):

        m = re.search(r"^(\d\d\d\d\d\d\d\d) +(\S+) +(\S*) +(\S+) +(\-?\d+\.\d\d) +(.*)$",zeile)
        if m:
            self.buchungen.append([m.group(1),m.group(2),m.group(3),m.group(4),m.group(6)])
            return True
        else:
            return False
            
    def parse_zeile_for_uktosaldo (self,zeile):

        m = re.search(r"^(\S+) (\S*) +(\-?\d+\.\d\d)$",zeile)
        if m:
            self.uktosalden[m.group(1)] = m.group(3)
            if not m.group(2) == "":
                self.uktonames[m.group(1)] = m.group(2)
            return True
        else:
            return False
            
#    def write_kto (self):
#
#        text = self.comment + "\n\n" + self.interval + "   (---PARENT------KTOID---)\n\n" + 
        
    def sort (self,pattern):
    
        text = open(file).read()
        text_match = []
        text_rest  = []
        
        for zeile in text.split("\n"):
            if re.search(pattern,zeile,re.IGNORECASE):
                text_match.append(zeile)
            else:
                text_rest.append(zeile)
                
        text = "\n".join(text_rest) + "\n" + "\n".join(text_match) + "\n"
        if os.path.isfile(file+"~"):
            os.unlink(file+"~")
        os.rename(file,file+"~")
        open(file,"w").write(text)
        
        for zeile in open(ktofile[0]).read().split("\n"):

            if self.parse_zeile_for_comment(zeile):
                continue
            if self.parse_zeile_for_buchung(zeile):
                continue
            if self.parse_zeile_for_uktosaldo(zeile):
                continue


#******************************************************************************

class Account3 (object):

    def __init__ (self,dbfile):
    
        self.mark()
        if not dbfile == ":memory:":
            dbfile = self.home+"/"+dbfile
        self.db             = sqlite3.connect(dbfile)
        self.db.row_factory = sqlite3.Row

        cursor = self.db.cursor()
        cursor.execute("create table if not exists buchungen " +
             "(DATUM,BETRAG,KTOA,KTOB,REMARK,ID default '')")
        cursor.execute("create table if not exists ktoname " +
             "(KTO,NAME,DATUMA,DATUMB)")

        try:
            cursor.execute("create index DATUM_IDX  on buchungen (DATUM)"   )
            cursor.execute("create index KTOA_IDX   on buchungen (KTOA)"    )
            cursor.execute("create index KTOB_IDX   on buchungen (KTOB)"    )
        except Exception as e:
            pass

        try:
            cursor.execute("create index KTO_IDX    on ktoname   (KTO)"     )
            cursor.execute("create index DATUMA     on ktoname   (DATUMA)"  )
        except Exception as e:
            pass

        self.ktoname_reverse = {}
        if 'ktoname' in vars(self.rules):
            self.ktoname = {}
            for o in self.rules.ktoname.split("\n"):
                m = re.search(r"^(.*)__(.*)$",o.strip())
                if m:
                    self.ktoname[m.group(1)] = m.group(2)
                    self.ktoname_reverse[m.group(2)] = m.group(1)

        self.mark("0. Datenbank einrichten.")

#********************************************************************************


#********************************************************************************


    def sort (self,pattern,file):
    
        text = open(file).read()
        text_match = []
        text_rest  = []
        
        for zeile in text.split("\n"):
            if re.search(pattern,zeile,re.IGNORECASE):
                text_match.append(zeile)
            else:
                text_rest.append(zeile)
                
        text = "\n".join(text_rest) + "\n" + "\n".join(text_match) + "\n"
        if os.path.isfile(file+"~"):
            os.unlink(file+"~")
        os.rename(file,file+"~")
        open(file,"w").write(text)
        

#********************************************************************************

    def cx (self,ukto_new,chdir=True,interval=None):
    
        ukto_new = re.sub(r"[\\\/]$","",ukto_new)
        erg      = None
        
        if not interval:
            m = re.search(r"(^|-|\\|\/)(19|2\d)(\d\d)(-|\/|\\|$)",os.path.relpath(".",self.home)+"/"+ukto_new)
            if m:
                interval = m.group(2)+m.group(3)
            else:
                interval = "1000,2999"
            
        if re.search(r"^\d\d\d\d$",ukto_new) and 1900 < int(ukto_new) < 2999: # os.path.relpath(".",self.home) == ".":
            interval = ukto_new

        m = re.search(r"^(.*)\.(kto|xls|xlsx)$",ukto_new)
        if m:
            ending    = m.group(2)
            ukto_dir  = m.group(1)
            ukto_path = ukto_dir
        else:
            ending    = ""
            ukto_dir  = ukto_new
            ukto_new  = ukto_new + ".kto"
            ukto_path = self.ktopath(".") + ukto_dir

        m = re.search(r"^(.*)(\_\_.*)$",ukto_dir)
        if m:
            ukto      = m.group(1)
            ukto_name = m.group(2)
        else:
            ukto      = ukto_dir
            ukto_name = ""

        if re.search(ukto_name[2:]+"$",ukto):
            ukto_name = ""

        ukto_path = re.sub(r"(\_\_.*)$","",ukto_path)
        ukto_path = re.sub(r"(^-|-$)","",ukto_path,9999)

        ktotext    = ukto_path
        startdatum = "00000000"
        enddatum   = "99999999"
        m         = re.search(r"^(.*?)\-(.*)$",ukto_path)
        if m:
            ktotext    = m.group(2)
            if re.search(r"^[12]\d\d\d",m.group(1)):
                startdatum = m.group(2) + "0000"
                enddatum   = m.group(2) + "9999"
        
        ktotext = re.sub("-"+interval,"",ktotext)
        ktotext = re.sub(r"^-","",ktotext)

        name1 = self.get_ktoname("-"+ktotext,startdatum,enddatum)
        if name1:
            ukto_name = "__" + name1
            ukto_dir  = ukto + ukto_name

        if ktotext == interval:
            ktotext = "   "
        ktotext   = self.rules.header + "\n" + ("=" * len(self.rules.header) ) + "\n\n" + ktotext
        ktotext   = ktotext + "  ()  " + interval + "  0.00 \n"
        
        if ending == "":   #  Directory ist addressiert

            for dir in glob.glob(ukto+"*"):
                if os.path.isdir(dir) and not "-" in re.sub("^"+ukto,"",dir):
                    if ukto_name == "":
                        ukto_dir = dir
                    os.rename(dir,ukto_dir)
                    break
            if not os.path.isdir(ukto_dir):
                os.mkdir(ukto_dir)

            file1 = ukto_dir+"/"+ukto_path+ukto_name+".kto"
            for file in glob.glob(ukto_dir+"/*.kto"):
                m = re.search(r"^(.*)[\\\/](.*)$",file)
                if os.path.isfile(file):
                    if m and len(interval) == 4 and not interval == m.group(2)[0:4]:  #  Datum muss passen
                        print ("REMOVE",file)
                        os.remove(file)
                    else:
                        if ukto_name == "":
                            file1 = file
                        os.rename(file,file1)
                        break
            if not os.path.isfile(file1):
                open(file1,"w").write(ktotext)
            if chdir:
                open(ukto_dir +"/__cd_dir__.sh","w").write('''
cd ''' + ukto_dir + ''' 2> /dev/null
# source_ea sync *.kto
rm __cd_dir__.* 2> /dev/null
''')

        else:
        
            file1 = ukto_path+ukto_name+"."+ending
            for file in glob.glob(ukto_path+"*.kto"):
                if os.path.isfile(file):
                    m = re.search(r"^(.*)[\\\/](.*)$",file)
                    if m and len(interval) == 4 and not interval == m.group(2)[0:4]:  #  Datum muss passen
                        print ("REMOVE",file)
                        os.remove(file)
                    else:
                        if ukto_name == "":
                            file1 = file
                        print(file1)
                        os.rename(file,file1)
                        break
            if not os.path.isfile(file1):
                open(file1,"w").write(ktotext)

        return(file1)

#********************************************************************************

    def ce (self,ukto_new,interval=None):
    
        ukto_new = re.sub(r"[\\\/]$","",ukto_new)
        self.cx(ukto_new)
        os.system("joe " + self.tmp[0])
        self.cx(ukto_new)
        
#        os.unlink(self.tmp[0])
        try:
            os.rmdir(self.tmp[1])
        except:
            pass

            
#********************************************************************************

    def cxx (self,*pars):
    

        pars = ['*/*/*/*/*/*/*/*/*/*/*',
                '*/*/*/*/*/*/*/*/*/*',
                '*/*/*/*/*/*/*/*/*',
                '*/*/*/*/*/*/*/*',
                '*/*/*/*/*/*/*',
                '*/*/*/*/*/*',
                '*/*/*/*/*',
                '*/*/*/*',
                '*/*/*',
                '*/*',
                '*']
                
        for pp in pars:
            for ukto_new1 in glob.glob(pp):
                if "EXCLUDE" in ukto_new1 or "NOTVALID" in ukto_new1:
                    continue
                ukto_new1 = re.sub(r"[\\\/]$","",ukto_new1)
                if os.path.isdir(ukto_new1):
                    m = re.search(r"^(.*)[\\\/](.*)",ukto_new1)
                    if m:
                        ukto_new2 = m.group(2)
                        dir2      = m.group(1)
                    else:
                        ukto_new2 = ukto_new1
                        dir2      = "."

                    homepath = os.path.abspath(".")
                    os.chdir(dir2)
                    self.cx(ukto_new2,False)
                    os.chdir(homepath)

#********************************************************************************


    def xxallocate_names (self,aktdir=None):

        if aktdir == None:
            self.tmpnames = {}
            self.allocate_names(".")
            cursor        = self.db.cursor()
            cursor.execute("delete from ktoname")
            for entry in self.tmpnames:
                entry1 = entry.split(",")
                cursor.execute("insert into ktoname (KTO,NAME,DATUMA,DATUMB) values ('" +
                               entry1[0] + "','" + self.tmpnames[entry] + "','" + 
                               entry1[1] + "','" + entry1[2] + "')")
            self.tmpnames = {}
            self.db.commit()
            return()


        for entry in os.listdir(aktdir):
            if not entry[0] == ".":
                if os.path.isdir(aktdir+"/"+entry):
                    self.allocate_names(aktdir+"/"+entry)
#                    m = re.search(r"^(.*)\_\_(.*)$",entry)
#                    if m:
#                        self.tmpnames[ukto+",19000000,29990000"] = m.group(2)
                else:
                    m = re.search(r"^(.*)\_\_(.*)\.(.*)$",entry)
                    if m:
                        text = open(aktdir+"/"+entry).read()[0:1000]
                        m1   = re.search(r"^(.*?)\n(\S+)[^\n]*?\) +(\S+)",text,re.DOTALL)
                        if m1:
                            ukto     = re.sub(r"\-[123456789IJKLMNPS]$","",m1.group(2))
                            interval = re.sub(r"\-[123456789IJKLMNPS]$","",m1.group(3))
                            m2       = re.search(r"^(.*),(.*)$",interval)
                            if m2:
                                int_start = m2.group(1)
                                int_ende  = m2.group(2)
                            else:
                                int_start = interval
                                int_ende  = interval
                            int_start = (int_start+"00000000")[0:8]
                            int_ende  = (int_ende+"99999999") [0:8]
                            self.tmpnames["-"+ukto+","+int_start+","+int_ende] = m.group(2)


#********************************************************************************

    def clear (self,pattern):
    
        zaehler = 0
        while (0 == 0):
            for file in (glob.glob(pattern+(("*/")*zaehler)+"*.kto")):
                os.unlink(file)
            zaehler = zaehler + 1
            if zaehler > 15:
                break

#********************************************************************************

    def sync (self,*pars):
    
        self.cxx()

        pars1   = []
        relhome = os.path.relpath(self.home,".")

        if len(pars) == 0:
            pars = ["****************.kto"]

        for par in pars:
            par = re.sub(r"\\","",par)
            par = re.sub(r"^\.\.\.",relhome,par)
            m = re.search(r"^(.*?)(\*\*+)(.*)$",par)
            if m:
                zaehler = 0
                while (0 == 0):
                    zaehler = zaehler + 1
                    if zaehler > len(m.group(2)):
                        break
                    pars1.append(m.group(1)+"/".join(["*"]*zaehler)+m.group(3))
            else:
                pars1.append(par)

#        'print (pars,pars1)
        
        if len(pars1) == 0:
            pars1 = ['*.kto','*.xlsx']
        ktofiles = []
        for par in pars1:
            files = glob.glob(par)
            for file in files:
                if not file in (ktofiles) and not "EXCLUDE" in file:
                    ktofiles.append(file)  #os.path.relpath(file))
        ktodatas = []

        for ktofile in ktofiles:
            if re.search(r"\.kto$",ktofile):
                ktodatas.append( { "KTOFILE": ktofile, "ROUNDS": 10 } )

        ktodatas.sort(key=lambda x:x['KTOFILE'])
            
        rounds = 0
        rtime1 = time.clock()
        rtime0 = rtime1
        while (0 == 0):

            rounds   = rounds + 1
            maxsteps = 1

            print ("")
            print ("=========================================")
            print ("START ROUND " + str(rounds))
            print ("=========================================")

            time.sleep( 0.03 * (rtime1 - rtime0) )  #  kleine Atempause fuer die Anzeige
            
            for ktodata in ktodatas:
        
                print ("")
                print (ktodata['KTOFILE'])
                if 'FUNC' in ktodata:
                    del ktodata['FUNC']
                ktodata["STEPS"]         = 0
                ktodata['RULES_APPLIED'] = False
                while (0 == 0):
                    if 'KTODIR' in ktodata and os.path.isfile(ktodata['KTODIR'] + "/__skip__.txt"):
                        print ("Skip-File found!")
                        break
                    ktodata["STEPS"] = ktodata["STEPS"] + 1
                    self.read_kto(ktodata,rounds)
                    if not "PROCEED" in self.dbsync(ktodata,rounds):
                        break
                    if not ktodata["MERGE"] == "":
                        self.mark("Merge-Conflict: " + ktodata["MERGE"])
                        break
                    if ktodata["STEPS"] > 20:
                        time.sleep(1)
                        open(ktodata['KTODIR'] + "/__skip__.txt","w").write("skip\n")
                        break
                    if ktodata["STEPS"] > 30:
                        break
                maxsteps = max(maxsteps,ktodata["STEPS"])
                

            rtime0 = rtime1
            rtime1 = time.clock()
            print ("Benoetigte Zeit: " + ("%7.2f" % (rtime1 - rtime0)) + " Sekunden.")

            if maxsteps < 2:
                break     
            
        merge_conflict_files = []
        for ktodata in ktodatas:
        
            if not ktodata["MERGE"] == "": #  and not ktodata['CONTENT'] == open(ktodata['KTOFILE']).read():
                zaehler = "001"  
                while (0 == 0):
                    if os.path.isfile( ktodata["KTOFILE"] + "." + zaehler ):
                        zaehler = ("%03u" % (int(zaehler) + 1) )
                    else:
                        break
                merge_conflict_files.append( ktodata['KTOFILE'] + "." + zaehler )
                open( merge_conflict_files[-1] , "w").write( ktodata['CONTENT'] )
            else:
                open( ktodata['KTOFILE'], "w").write(ktodata['CONTENT'] )
#                if len(glob.glob(ktodata['KTODIR']+"/*.kto") == 1:
#                    for udir in ktodata['UKTODIRS']:
#                        
#                        if not os.path.isdir(ktodata['KTODIR']+"/"+udir):
#                            os.mkdir(ktodata['KTODIR']+"/"+udir)

        self.db.commit()
        print ("=========================================")
        if rounds == 1:
            rounds = str(rounds) + " Round."
        else:
            rounds = str(rounds) + " Rounds."
        self.mark("I. Datenbank Commit. " + rounds)
        if len(merge_conflict_files) > 0:
            print ("\n" + "\n".join(merge_conflict_files) + "\n\n")

#********************************************************************************

    def read_kto (self,ktodata,rounds=0):
    
        if ktodata['STEPS'] > 1:
            self.mark("%-70s" % ("------------ " + ktodata['FILENAME'] + " " +
                           str(rounds) + " " + str(ktodata['STEPS']) + " -------------------") )
#        else:
#            self.mark(ktodata["KTOFILE"])

        if not "KTODIR" in ktodata:

            m = re.search(r"^(.*)[\\\/](.*)",ktodata['KTOFILE'])
            if m:
                ktodata['KTODIR']   = m.group(1)
                ktodata['FILENAME'] = m.group(2)
            else:
                ktodata['KTODIR']   = "."
                ktodata['FILENAME'] = ktodata['KTOFILE']

            self.make_addtext(ktodata)

            ktodata['CONTENT']     = open(ktodata['KTOFILE']).read()
            ktodata['MAXA']        = 10
            ktodata['MAXB']        = 10
            ktodata['IMPORT1']     = ""
            ktodata['DBCHANGED']   = True
            ktodata['DBHASH']      = "-"
            ktodata['KTOPATH']     = self.ktopath(ktodata['KTODIR'])
            ktodata['RULES_APPLY'] = 0
            self.mark("A. Einlesen Konto.")

        else:
            pass
        
        if not 'INT' in ktodata:
            self.rules.rule__raw(ktodata)        

#        print ("Apply",ktodata['RULES_APPLY'])
        if 'FUNC' in ktodata and ktodata['DBCHANGED'] and ktodata['RULES_APPLY'] >= 1 and ktodata['RULES_APPLY'] < 2:
            for func in (ktodata['FUNC'],ktodata['FUNC1'],ktodata['FUNC2'],ktodata['FUNC3']):
                if func in self.rules.__class__.__dict__:
                    self.rules.__class__.__dict__[func](self.rules,ktodata)
                    self.mark("B. Rules anwenden: " + func)
                    ktodata['RULES_APPLIED'] = True
                    break
            
        m = re.search(r"^(.*?)\((.*?)\)(.*)$",ktodata['CONTENT'],re.DOTALL)

        text1              = m.group(1)
        text2              = m.group(3)
        ktodata['DBHASH']  = m.group(2)[0:6]
        ktodata['KTOHASH'] = m.group(2)[6:]

        text3 = re.sub("\n\d\d\d\d\d\d\d\d.*?Saldovortrag","",text2)
        if re.search(r"\n\d\d\d\d\d\d\d\d +\-?\d",text3):
            t = "___CONTAINS___BUCHUNGEN___"
        else:
            t = ""
        t                  = t + text1 + text2 + ktodata['ADDTEXT']
        ktodata['NEWHASH'] = self.compute_ktohash(ktodata,t)
        ktodata['IMPORT0'] = ktodata['IMPORT1']
        t                  = re.sub(" +","",t.strip(),99999999)
        ktodata['IMPORT1'] = self.compute_ktohash(ktodata,t)
        ktodata["INT"]     = ""

        m = re.search(r"^(.*?\n|)(\S*) +$",text1,re.DOTALL)
        ktodata['HEADER']  = m.group(1)
        ktodata['UKTO']    = m.group(2)
        m1 = re.search(r"^(.*)\-([123456789ABCIJKLMNPS])$",ktodata['UKTO'])
        if m1:
            ktodata['INT']  = m1.group(2)
            ktodata['UKTO'] = m1.group(1)

        ktodata['UKTO1'] = "-" + ktodata['UKTO']
        if ktodata["UKTO1"] == "-":
            ktodata["UKTO1"] = ""


        m = re.search(r"^ *([0123456789,]*)\s* +([^\n]*?)(\-?\d+\.\d\d) *\n\s*(.*)$",text2,re.DOTALL)
        ktodata['INTERVAL']  = m.group(1)
        m1 = re.search(r"^(.*)\-([123456789IJABCKLMNPS])$",ktodata['INTERVAL'])
        if m1:
            ktodata['INT']      = m1.group(2)
            ktodata['INTERVAL'] = m1.group(1)
        ktodata['NAME']      = m.group(2)
        ktodata['SOLLWERT']  = m.group(3)
        ktodata['BUCHUNGEN'] = m.group(4)
        ktodata['MERGE']     = ''

#        m = re.search(r"^(\d\d\d\d)\-$",ktodata['FILENAME'][0:5])    #   Hack um Kontodateien kopieren zu koennen
#        if m and re.search(r"^(\d\d\d\d)$",ktodata['INTERVAL']):
#            if not m.group(1) == ktodata['INTERVAL']:
#                ktodata['INTERVAL']  = m.group(1)
#                ktodata['BUCHUNGEN'] = ""

            
        m = re.search(r"^(.*),(.*)$",ktodata['INTERVAL'])
        if m:
            ktodata['START'] = m.group(1)
            ktodata['ENDE']  = m.group(2)
        else:
            ktodata['START'] = ktodata['INTERVAL']
            ktodata['ENDE']  = ktodata['INTERVAL']
        ktodata['START']   = (ktodata['START'] + "00000000")[0:8]
        ktodata['ENDE']    = (ktodata['ENDE']  + "99999999")[0:8]

        if not ktodata['INT'] == "":    #   Zeitraum bestimmen
            intv = { "1" : ["0101","0199"],
                     "2" : ["0201","0299"],
                     "3" : ["0301","0399"],
                     "4" : ["0401","0499"],
                     "5" : ["0501","0599"],
                     "6" : ["0601","0699"],
                     "7" : ["0701","0799"],
                     "8" : ["0801","0899"],
                     "9" : ["0901","0999"],
                     "A" : ["1001","1099"],
                     "B" : ["1101","1199"],
                     "C" : ["1201","1299"],
                     "I" : ["0101","0399"],
                     "J" : ["0401","0699"],
                     "K" : ["0701","0999"],
                     "L" : ["1001","1299"],
                     "M" : ["0101","0699"],
                     "N" : ["0701","1299"],
                     "P" : ["0101","1299"] } [ ktodata['INT'] ]
            ktodata["START"] = ktodata["START"][0:4] + intv[0]
            ktodata["ENDE"]  = ktodata["ENDE"][0:4]  + intv[1]

#        print (ktodata["START"],ktodata["ENDE"])

        ktodata['FUNC']    = "rule__" + re.sub(r"-","_",re.sub(r"_","__",ktodata['UKTO'],99),99)
        ktodata['FUNC1']   = re.sub(r"^(.*)\_(.*)$",        "\\1"+"_xxx",        ktodata['FUNC'])
        ktodata['FUNC2']   = re.sub(r"^(.*)\_(.*\_.*)$",    "\\1"+"_xxx_xxx",    ktodata['FUNC'])
        ktodata['FUNC3']   = re.sub(r"^(.*)\_(.*\_.*\_.*)$","\\1"+"_xxx_xxx_xxx",ktodata['FUNC'])

        while not 'FILENAME1' in ktodata:
            ktodata['FILENAME1'] = ktodata['FILENAME']
            pathnew = ktodata['INTERVAL']+"-"+ktodata['UKTO']+'-'+ktodata['INT'] + "-"
            ul      = min(len(ktodata['KTOPATH']),len(pathnew))
            if ul > 0 and ktodata['KTOPATH'][0:ul] == pathnew[0:ul]:
                m1 = re.search(r"^(.*)\.(kto|xls|xlsx)$",ktodata['FILENAME'])
                if m1:
                    filename = m1.group(1)
                    ending   = m1.group(2)
                    filename = re.sub(r"^(.*)\_\_(.*)$","\\2",filename)
                    if not ktodata['INT'] == "":
                        ktodata['FILENAME1'] = ktodata['INTERVAL']+"-"+ktodata['UKTO']
                        print (filename)
                        if ktodata['INT']:
                            ktodata['FILENAME1'] = ktodata['FILENAME1'] + "-"  + ktodata['INT']
                        if re.search(filename+"$",ktodata['INTERVAL'] + "-" + ktodata['UKTO']) or "-" in filename:
                            filename = ""
                        if not filename == "":
                            ktodata['FILENAME1'] = ktodata['FILENAME1'] + "__" + filename
                        if ktodata['FILENAME1'] == ktodata['INT'] + "-__" + ktodata['INT']:
                            ktodata['FILENAME1'] = ktodata['FILENAME']
                        ktodata['FILENAME1'] = ktodata['FILENAME1'] + "." + ending

            try:
                os.rename(ktodata['KTODIR']+"/"+ktodata['FILENAME'],ktodata['KTODIR']+"/"+ktodata['FILENAME1'])
                ktodata['FILENAME'] = ktodata['FILENAME1']
                ktodata['KTOFILE']  = ktodata['KTODIR']+"/"+ktodata['FILENAME']
            except:
                pass
            break

        o = ""
        if not ktodata['FILENAME'] == ktodata['FILENAME1']:
            o = " ---> Neuer Kontoname: " + ktodata['FILENAME1']
        self.mark("C. Analysieren Kontotext." + o)
        ktodata['RULES_APPLY'] = ktodata['RULES_APPLY'] + 1


#*************************************************************************************

    def make_addtext (self,ktodata):

        addfiles  = glob.glob(ktodata['KTODIR']+"/*")
        addfiles.sort()
        addtext   = self.rules.addtext_locale
        if "AUX" in ktodata['KTOFILE']:
            ktodata['ADDTEXT'] = addtext
            return()

        for addfile in addfiles:

            if not os.path.isfile(addfile):
                continue
            if re.search(r"\.(kto[\.\d]*|kto|py|pyc|db|db-journal|pdf.*|aux|dvi|gif|jpg|zip|xls|xlsx|doc|docx|\.\d+)$",
                        addfile) or "EXCLUDE" in addfile or "NOTVALID" in addfile or "~" in addfile:
                if not "AUX" in addfile:
                    continue
            if re.search(r"(__pycache__)$",addfile):
                continue
            m = re.search(r"^(.*)[\\\/](.*)$",addfile)
            if not "." in m.group(2):
                continue
            #print ("ADD1",addfile,m.group(2))
            if os.path.isfile(addfile):
                try:
                    addtext = addtext + str( base64.urlsafe_b64encode(
                               hashlib.md5(open(addfile,'r').read().encode("utf8")).digest()),"ascii" )
                except:
                    print (addfile," is not utf8")
                    exit()
            if m:
                addtext =  addtext + m.group(2)
            else:
                addtext =  addtext + addfile

        ktodata['ADDTEXT']     = addtext

#*************************************************************************************

    def ktopath (self,dir):

        newktopath = os.path.relpath(dir,self.home) 
        if newktopath == ".":
            newktopath = ""
        newktopath = re.sub(r"\_\_(.*?)(\\|\/|$)","-",newktopath,9999)
        newktopath = re.sub(r"[\\\/]","-",newktopath,9999)
        newktopath = newktopath.strip("-")
        newktopath = re.sub(r"^\-+","",newktopath,9999) + "-"
        return(newktopath)

#*************************************************************************************

    def dbsync (self,ktodata,rounds):
    
        dbhash = self.compute_dbhash(ktodata)

#        print (ktodata['NEWHASH'])
        if ktodata['NEWHASH'] == "xxxxxx":
            ktodata['DBHASH']  = "."
        if dbhash == "xxxxxx" and ktodata['NEWHASH'] == "xxxxxx":
            ktodata['NEWHASH'] = "."
        
        if 'AUX' in ktodata['KTOFILE']:
            ktodata['RULES_APPLY'] = 99999999
        
        if ktodata['DBHASH'] == "." and ktodata['NEWHASH'] == ".":
            if rounds > 1 or ktodata['ADDTEXT'] == "":
                self.mark("H. Konto ist aktuell. Steps: " + str(ktodata["STEPS"]) +".")
                return("Kto is up-to-date")       #  Dann nichts machen

        self.mark("   --> DBHASH: " + ktodata['DBHASH'] + ", DB_NEW: " + dbhash +
                     ", KTOHASH: " + ktodata['KTOHASH'] + ", NEW_HASH:  " + ktodata['NEWHASH'] +
                     ", IMPORT0: " + ktodata['IMPORT0'] + ", IMPORT1:  " + ktodata['IMPORT1'])

             # Kontofile - oder die zugehoerigen Dateien, oder Readonly-File - haben sich nicht geaendert:

        if ktodata['KTOHASH'] == ktodata['NEWHASH'] or ktodata['NEWHASH'] == 'xxxxxx':

            if dbhash == ktodata['DBHASH'] and ( not dbhash == "xxxxxx" or ktodata['KTOHASH'] == "xxxxxx" ): 
                         #  Kontoeintraege in der Datenbank haben sich auch nicht geaendert.
                self.mark("H. Konto ist aktuell. Steps: " + str(ktodata["STEPS"]) +".")
                return("Kto is up-to-date")       #  Dann nichts machen

            else:
                ktodata['DBHASH'] = dbhash
                self.get_from_db(ktodata)      #   Kontotext updaten aus der Datenbnk
                self.mark("G1. Kontotext neu schreiben.")

        else:  # Kontofile - oder die zugehoerigen Dateien - haben sich geaendert

            if dbhash == ktodata['DBHASH'] or dbhash == "xxxxxx":  #  Datenbankdaten haben sich nicht geaendert
                merge_result = ""
                
            else:
            
                cursor = self.db.cursor()
                qstr   =        " select DATUM,BETRAG,KTOA,KTOB,REMARK from buchungen "
                qstr   = qstr + " where (KTOA like '" + ktodata['UKTO1'] + "%' or KTOB like '" + ktodata['UKTO1'] + "%')"
                qstr   = qstr + " and DATUM >= '" + ktodata['START'] + "' and DATUM <= '" + ktodata['ENDE'] + "' "
                qstr   = qstr + " order by DATUM,KTOA,KTOB" 
                cursor.execute(qstr)
                merge_result = self.rules.merge(ktodata,cursor)
                self.mark("E. Mergen. " + merge_result)
                
                if os.path.isfile(ktodata['KTOFILE']+".001"):
                    os.unlink(ktodata['KTOFILE']+".001")
                    merge_result = ""
                
            if merge_result == "":   #  aus den kontodaten in die DB importieren

                if not ktodata['IMPORT1'] == ktodata['IMPORT0']:
                    if not 'AUX' in ktodata['KTOFILE']:
                        self.db.cursor().execute("delete from buchungen where (KTOA like '" +    
                                    ktodata["UKTO1"] + "%' OR " + " KTOB like '" + ktodata["UKTO1"] + "%') " +
                                        " and DATUM >= '" + ktodata['START'] + "' and DATUM <= '" + ktodata['ENDE'] + "' ")

                        unique_strings = {}
                        ul             = len(ktodata['UKTO1'])
                        for zeile in ktodata['BUCHUNGEN'].split("\n"):
                            m = re.search(r"^(\d\d\d\d\d\d\d\d) +(\S+) +(\S*) +(\S+) +(\-?\d+\.\d\d) +(.*)$",zeile)
                            if m:
                                datum  = m.group(1)
                                betrag = "%3.2f" % eval(m.group(2))
                                remark = m.group(6)
                                uniqu  = []

                                ktoa   = "-" + m.group(3)
                                if ktoa == "--":
                                    ktoa  = ktodata['UKTO1']
                                elif ktoa[0:2] == "--":
                                    ktoa  = ktodata['UKTO1'] + ktoa[1:]
                                elif not ktoa[0:ul] == ktodata['UKTO1']:
                                    uniqu.append(ktoa)

                                ktob   = "-" + m.group(4)
                                if ktob == "--":
                                    ktob = ktodata['UKTO1']
                                elif ktob[0:2] == "--":
                                    ktob = ktodata['UKTO1'] + ktob[1:]
                                elif not ktob[0:ul] == ktodata['UKTO1']:
                                    uniqu.append(ktob)

                                if "Saldovortrag" in remark or ktob == "--11-1805":
                                    continue
 
                                if ktoa > ktob:    #  um Eindeutigkeit zu schaffen. Sonst kann es durch
                                    o      = ktoa  #  Hin- und Herschwingen zu Endlos-Loops kommen
                                    ktoa   = ktob
                                    ktob   = o
                                    betrag = re.sub(r"^--","","-"+betrag)
                                if uniqu == []:
                                    uniqu = [ktoa,ktob]
                                uniqu1 = []
                                for k in uniqu:
                                    uniqu1.append(re.sub(r"\-\d\d\d\d\d$","-BETRAG",k))

                                betrag1 = re.sub("\-","",betrag) + ","
                                remark1 = "," + re.sub(r"[\+\- ]","",remark,9999)
                                uniqu   = betrag1 + ",".join(uniqu1) + remark1
                                if datum in unique_strings:  #  to avoid double entries
                                    if uniqu in unique_strings[datum]:
                                        continue
                                    else:
                                        unique_strings[datum].append(uniqu)
                                else:
                                    unique_strings[datum] = [uniqu]  #  ,betrag1+"-13-9999"+remark1]
#                                print ("<<<----")
#                                if ktoa < ktob:
#                                    id = datum + "," + betrag1 + "," + ktoa + "," + ktob + "," + remark1
#                                else:
#                                    id = datum + "," + betrag1 + "," + ktob + "," + ktoa + "," + remark1
#                                id = str( base64.urlsafe_b64encode(hashlib.md5(id.encode("utf8")).digest())[0:9], "ascii" )
                                qstr = ("insert into buchungen (DATUM,BETRAG,KTOA,KTOB,REMARK,ID) values ('" + datum +
                                          "'," + betrag + ",'" + ktoa + "','" + ktob + "','" +
                                          re.sub(r"'","",remark,9999) + "','" + 'a' + "')")
#                                print(qstr)
                                self.db.cursor().execute(qstr)
                
                        ktodata['DBCHANGED'] = True # not ( ( ktodata['DBHASH'] == o ) and ( ktodata['IMPORT0'] == ktodata['IMPORT1'] ) )
                        self.mark("F. Importieren in die Datenbank.")
                        o = self.compute_dbhash(ktodata)
                        ktodata['DBHASH'] = o
                else:
                    ktodata['DBCHANGED'] = False

#                    self.mark("F. Kein Datenbank-Update noetig.")
                    
                self.get_from_db(ktodata)      #   Kontotext updaten aus der Datenbnk
#                print(ktodata['CONTENT'])
                self.mark("G2. Kontotext neu schreiben.")

            else:
                ktodata['MERGE'] = merge_result
 
        return("PROCEED.")                    

#********************************************************************************

    def dbhash (self,ukto):
    
        ktodata  = {  'UKTO1' : "-"+ukto, 'START': '00000000',  'ENDE' : '99999999' }
        print ( self.compute_dbhash(ktodata) )
                
        

#********************************************************************************

    def compute_dbhash (self,ktodata):

        qu_string = (" select  DATUM,BETRAG,KTOB,REMARK " +
                     " from buchungen " +
                     " where (KTOA like '" + ktodata['UKTO1'] + "%' or KTOB like '" + ktodata['UKTO1'] + "%') " +
                     " and DATUM >= '" + ktodata['START'] + "' and DATUM <= '" + ktodata['ENDE'] + "' " +
                     " order by DATUM,KTOA,KTOB,BETRAG,REMARK") 

#        print (qu_string)
        cursor = self.db.cursor()
        cursor.execute(qu_string)
        dbhash_new = [""]
        while (0 == 0):
            entry    = cursor.fetchone()
            if entry == None:
                break
            entry    = list(entry)
            entry[1] = "%3.2f" % entry[1]
            dbhash_new.append(re.sub(r" +"," "," ".join(list(entry)),99999999))
            
        if dbhash_new == [""]:
            dbhash_new = "xxxxxx"    #   wenn es gar keine Buchungen im Unterkonto gibt, dann dbhash auf "xxxxxx" setzen
        else:
            dbhash_new = base64.urlsafe_b64encode(hashlib.md5("".join(dbhash_new).encode("utf8")).digest())[0:6]
            dbhash_new = str(dbhash_new,"ascii")
        return(dbhash_new)

#********************************************************************************

    def compute_ktohash (self,ktodata,text):

        if not "___CONTAINS___BUCHUNGEN___" in text[0:36]:  #  wenn es gar keine Buchungen im Kontoauszug gibt
            erg = "xxxxxx"
        elif "<<<< HEAD" in text:
            erg = "xxxxxx"
        else:
            erg = str(base64.urlsafe_b64encode(hashlib.md5(text.encode("utf8")).digest()),"ascii" )[0:6]
        return(erg)
        
                                
#*******************************************************************************
        
    def get_from_db (self,ktodata):

        saldovortrag = []
        salden       = {}

        if ktodata["INT"] == "":
            saldovortrag = [0.0]            
            for kto in ("KTOA","KTOB"):
                cursor = self.db.cursor()
                qstr   =        " select sum(BETRAG) from buchungen "
                qstr   = qstr + " where (" + kto + " like '" + ktodata['UKTO1'] + "%')"
                qstr   = qstr + " and DATUM < '" + ktodata['START'] + "' "
                cursor.execute(qstr)
                o = cursor.fetchone()[0]
                if o:
                    saldovortrag[0] = saldovortrag[0] + float(o)
                saldovortrag[0] = -saldovortrag[0]
            salden[""] = saldovortrag[0]


        cursor = self.db.cursor()
        qstr   =        " select DATUM,BETRAG,KTOA,KTOB,REMARK from buchungen "
        qstr   = qstr + " where (KTOA like '" + ktodata['UKTO1'] + "%' or KTOB like '" + ktodata['UKTO1'] + "%')"
        qstr   = qstr + " and DATUM >= '" + ktodata['START'] + "' and DATUM <= '" + ktodata['ENDE'] + "' "
        qstr   = qstr + " order by DATUM,BETRAG,KTOA,KTOB" 
        cursor.execute(qstr)
        
        ukto1        = ktodata['UKTO1']
        ul           = len(ukto1)
        gesamt       = 0.00
        maxa         = 9
        maxb         = 9
        maxa0        = "%-" + str(ktodata['MAXA']) + "s"
        maxb0        = "%-" + str(ktodata['MAXB']) + "s"
        text_intern  = []
        text_extern  = []
        t            = ""
        datum0       = ""
        betrag0      = ""
        
        while (0 == 0):    #  Zeilen des Kontos schreiben
           
            if len(saldovortrag) > 0:
                entry        = [ ktodata['START'], saldovortrag[0], ktodata['UKTO1'], "-11-1805", "Saldovortrag"]
                saldovortrag = []
            else:
                entry = cursor.fetchone()
                if not entry:
                    break

            if t == "":
                if not "Saldovortrag" in entry[4] :
                    t = "___CONTAINS___BUCHUNGEN___"
                    
            betrag    = float(entry[1])
            ktoa      = entry[2]
            ktob      = entry[3]

            anz       = 0
            if ktoa[0:ul] == ukto1:
                ktoa = ktoa[ul:]
#                self.compute_salden(ktoa,betrag)
                if ktoa == "":
                    ktoa = "-"
                anz = anz + 1
            else:
                ktoa = ktoa[1:]
            if ktob[0:ul] == ukto1:
                ktob = ktob[ul:]
#                self.compute_salden(ktob,-betrag)
                if ktob == "":
                    ktob = "-"
                if anz == 0:
                    o      = ktoa
                    ktoa   = ktob
                    ktob   = o
                    betrag = -betrag
                anz = anz + 1
            else:
                ktob = ktob[1:]
         
            datum   = entry[0]
            
#            if datum == datum0 and betrag == betrag0 and abs(betrag) > 0.001:
#                print ("WARNING:",datum,("%13.2f"%betrag))
            datum0  = datum
            betrag0 = betrag
            
            remark  = entry[4]

            sortidx = datum
            m       = re.search(r"^(.*)? *\{\{SORTIDX\: +(.*?) *\}\} *(.*)$",remark)  #   proprietaeren Sort-Index herauslesen
            if m:
                remark  = (m.group(1) + " " + m.group(3)).strip()
                sortidx = sortidx + (m.group(2) + " "*40)[0:40]

            sortidx = sortidx + self.rules.sortidx(ktoa)
            m = re.search(r"^(\d\d? v\.?H\.? +.*\()(.*)\)",remark)
            if m:
                sortidx = sortidx + (m.group(2)+" "*40)[0:40] + "-Z"
            else:
                sortidx = sortidx + (re.sub(r"^(\+\-|\+\+)","",remark) + " "*40)[0:40] + "-A"

            sortidx = sortidx + ktob
            ktoa    = re.sub("^"+ukto1,"",ktoa)

            if ktoa == "":
                ktoa = "-"
                
            maxa    = max(maxa,len(ktoa))
            maxb    = max(maxb,len(ktob))
            buchung = datum +  " " + ("%13.2f" % betrag) + "  " + (maxa0 % ktoa)  + "  " +  (maxb0 % ktob) + "  "
            sp      = ""
            if re.search(r"^\d\d? +v\.?H\.? ",entry[4]):   #  USt-Einrueckung
                sp = "   "
            if anz == 1:
                text_extern.append([buchung,"  " + sp + remark,betrag,sortidx])
            else:
                text_intern.append([buchung + "         0.00  " + sp + remark,"","",sortidx])
            
        text_intern.sort(key=lambda x: x[3])
        text_extern.sort(key=lambda x: x[3])
        
#        self.mark("Start Salden")

        fac = 1
        for kto in ("KTOA","KTOB"):
            qstr   =        " select " + kto + ",sum(BETRAG) from buchungen "
            qstr   = qstr + " where " + kto + " like '" + ktodata['UKTO1'] + "%'"
            qstr   = qstr + " and DATUM >= '" + ktodata['START'] + "' and DATUM <= '" + ktodata['ENDE'] + "' "
            qstr   = qstr + " group by (" + kto + ")" 
            cursor.execute(qstr)
            while (0 == 0):
                entry = cursor.fetchone()
                if not entry:
                    break
                ukto   = entry[0][ul:]
                betrag = float(entry[1])
                while (0 == 0):
                    if not ukto in salden:
                        salden[ukto] = 0.00000001
                    salden[ukto] = salden[ukto] + fac * betrag
                    m = re.search(r"^(.*)\-(.*)$",ukto)
                    if m:
                        ukto = m.group(1)
                    else:
                        break
            fac = -1
#        self.mark("Ende Salden")

#        print (salden)

        ktoliste = []      #  Kontenliste sortieren
        for kto in salden.keys():
            ktoliste.append([kto,self.rules.sortidx(re.sub(r"^-","",kto))])
        ktoliste.sort(key=lambda x: x[1])
        konten = []

        text = []
        
        gesamt = 0.00
        for o in text_extern:
            gesamt = gesamt + float(o[2])
            text.append(o[0]+("%13.2f" % gesamt)+o[1])
        text.append("")

        for kto0 in ktoliste:
            kto  = kto0[0]
            kto2 = re.sub(r"^\-","",kto)
            sp   = re.sub(r"[^\-]","",kto)
            name = self.get_ktoname(ktodata['UKTO1']+kto,ktodata['START'],ktodata['ENDE'])
            if self.rules.empty_line_in_ktolist(kto2):
                text.append("")
            text.append( ("%-56s" % (("%-20s" % kto2) + name ) ) +
                                re.sub(r"\-","    ",sp,9999) + ("%13.2f" % salden[kto]) )
            if not "-" in kto2:
                if not name == "":
                    name = "__" + name
                if not 'UKTODIRS' in ktodata:
                    ktodata['UKTODIRS'] = {}
                ktodata['UKTODIRS'][kto2+name] =1
        text.append("")

        
        for o in text_intern:
            text.append(o[0])
        text.append("")

        if not ktodata['INT'] == "":
            o = "-" + ktodata['INT']
        else:
            o = ""
        
        text1 = ktodata['HEADER']
        text1 = text1 + ("%-30s" % (ktodata['UKTO'] + o)) + "  "
        text2 = "  "  + ktodata['INTERVAL'] + "  " + ("%13.2f" % float(gesamt) ) + "\n\n"
        text2 = text2 + "\n".join(text) + "\n"

        ktodata['NEWHASH'] = self.compute_ktohash(ktodata,t+text1+text2+ktodata['ADDTEXT'])
#        if ktodata['NEWHASH'] == "xxxxxx":
#            ktodata['NEWHASH'] = ktodata['KTOHASH']
#        print ("----->",123)        


        ktodata['CONTENT'] = text1 + "(" + ktodata['DBHASH'] + ktodata['NEWHASH'] + ")" + text2

        if not ktodata['MAXA'] == maxa or not ktodata['MAXB'] == maxb:
            ktodata['MAXA']    = maxa
            ktodata['MAXB']    = maxb
            ktodata['CONTENT'] = ktodata['CONTENT'] + " "  # damit der KTOHASH nicht mehr stimmt und neu berechnet wird


#********************************************************************************

    def get_ktoname (self,ukto,startdatum="00000000",enddatum="99999999"):

        ukto = re.sub(r"\-[123456789ABCIJKLMNPS]$","",ukto)

        if 'ktoname' in vars(self):
            if ukto[1:] in self.ktoname:
                return( self.ktoname[ukto[1:]] )
            return("")
           
        cursor = self.db.cursor()
        cursor.execute("select KTO,NAME,DATUMA,DATUMB from ktoname where KTO = '" + ukto + "'")
        intervals = []
        while (0 == 0):
            entry = cursor.fetchone()
            if not entry:
                break
            intervals.append([entry[1],entry[2],entry[3]])
        if len(intervals) > 0:
            interval = intervals[0]
            name     = interval[0]
        else:
            name = ""
        return(name)


#********************************************************************************

    def assign_ausgaben (self,ktodata,tmpkto,idbuch0):    #   Ordnet Ausgabenkonten automatisch zu
    
        buchgrp = {}  #  Hier werden die Buchhaltungsgruppen gehalten
        zeilen  = []
        idbuch  = []
        
        for o in re.sub(r"\n",",",idbuch0,9999,re.DOTALL).split(","):
            if not o.strip() == "":
                idbuch.append(o)

        zeilennr = 0
        for zeile in ktodata['CONTENT'].split("\n"):
            zeilen.append(zeile)
            m = re.search(r"^(\d\d\d\d\d\d\d\d) +(\-?\d+\.\d\d) +(\S*) +(\S+) +(\-?\d+\.\d\d) +(.*)$",zeile)
            if m:
                id1   = None
                parts = None
                for id in idbuch:
                    m1 = re.search(id,zeile,re.IGNORECASE)
                    if m1:
                        id1     = id
                        parts   = []
                        zaehler = 0
                        while (0 == 0):
                            zaehler = zaehler + 1
                            try:
                                parts.append(m1.group(zaehler))
                            except:
                                break
                        break
                if id1 == None:
                    parts = []
                    id1   = re.sub(r"[\+\-\. ]","",m.group(1)+m.group(2)+m.group(6),9999)
                if not id1 in buchgrp:
                    buchgrp[id1] = []
               
                ktob = m.group(4)
                if not ktob in self.ktoname:    #   Konto rueckwaerts ueber den Namen finden
                    m2 = re.search("^"+tmpkto+"\-(.*)$",ktob)
                    if m2:
                        pattern = m2.group(1)
                        if pattern in self.ktoname_reverse:
                            ktob = self.ktoname_reverse[pattern]
                        else:
                            for name in self.ktoname_reverse:
                                if pattern in name:
                                    ktob = self.ktoname_reverse[name]
                                    zeilen[-1] = ( m.group(1) + "  " + m.group(2) + "  " + m.group(3) + "  " +
                                                  ktob + "  " + m.group(5) + "  " + m.group(6))
                                    break
                
                buchgrp[id1].append(
                       { 'DATUM': m.group(1), 'BETRAG': m.group(2), 'KTOA': m.group(3), 'KTOB': ktob,
                         'REMARK': m.group(6), 'ZEILENNR': zeilennr, 'CHANGED': 0, 'PARTS': parts } )

            zeilennr = zeilennr + 1
            
        ust = {}
        for id in buchgrp:
            ust[id] = {}
            for entry in buchgrp[id]:
                m = re.search(r"^(\+\+|\+\-)",entry['REMARK'])
                if m:
                    ust[id][m.group(1)] = 1
            o = list(ust[id].keys())
            if len(o) == 1:
                ust[id] = o[0]
            else:
                ust[id] = ""

        zweiter_durchgang_erforderlich = False
        for id in buchgrp:

            alle_gegenkonten = {}  #  erstmal ueberhaupt alle gegenkonten zusammensammeln
            for entry in buchgrp[id]:
                o = re.sub(r"\-\d\d\d\d\d$","-BETRAG",entry['KTOB'])
                alle_gegenkonten[o] = 1
            alle_gegenkonten = list( alle_gegenkonten.keys() )
            alle_gegenkonten.sort()
            if len(alle_gegenkonten) > 2:
                print ("ALLE",alle_gegenkonten,id)
                if not idbuch0 == "":
                    zweiter_durchgang_erforderlich = True
            if len(alle_gegenkonten) > 1 and tmpkto in alle_gegenkonten:  #  wenn es mehrere gegenkonto gibt
                alle_gegenkonten.remove(tmpkto)       #  neben dem tmpkto, dann den gemeinsamen Start-String berechnen
                common_start = alle_gegenkonten[0]
                for o in alle_gegenkonten:
#                    print ("COMMON",common_start,o)
                    ul           = min(len(common_start),len(o))
                    common_start = common_start[0:ul]
                    o            = o[0:ul]
                    while (0 == 0):
                        if common_start == o:
                            break
                        common_start = common_start[:-1]
                        o            = o[:-1]
                        
#                print ("COMMON",common_start)
                if not len(alle_gegenkonten) == 1:
                    for entry in buchgrp[id]:   #  ueberpruefen, ob die Theorie stimmt, nach der die Gegenkonten gemacht sind
                        if not entry['KTOB'] == tmpkto:  #  bei allen schon zugeordneteten Konten
                            o1 = ""
                            if not len(alle_gegenkonten) == 1:
                                o1 = "".join( entry['PARTS'] )
                            if not entry['KTOB'] == common_start + o1:
                                common_start = None
                                break

                if common_start == None:
                    continue

                for entry in buchgrp[id]:  #  wenn die Theorie stimmt, dann mit den unbestimmenten Gegenkonten
                    if entry['KTOB'] == tmpkto:    #  genauso verfahren und zuordnen
                        o = ""
                        if not re.search(r"^(\,+\+|\+\-)",entry['REMARK']):
                            o = ust[id]
                        ktob = common_start
                        if not len(alle_gegenkonten) == 1:
                            ktob = ktob + "".join( entry['PARTS'] )
                        m = re.search(r"^(.*)(\-BETRAG$)",ktob)
                        if m:
                            betrag = ("%8.7f" % float(entry['BETRAG'])).strip()
                            betrag = re.sub(r"[\-\.]","",betrag)[0:5]
                            ktob   = m.group(1) + "-" + betrag
                        zeilen[ entry['ZEILENNR'] ] = (
                                entry['DATUM'] + "  " + entry['BETRAG'] + "  " +
                                entry['KTOA'] + "  " + ktob + "  0.00  " + o + entry['REMARK'] )

        ktodata['CONTENT'] = "\n".join(zeilen)

        if zweiter_durchgang_erforderlich:
            self.assign_ausgaben(ktodata,tmpkto,"")

#********************************************************************************
        
    def normalize_text (self,text):
    
        text = re.sub(r"ä",   "ae",text,99999999)
        text = re.sub(r"ö",   "oe",text,99999999)
        text = re.sub(r"ü",   "ue",text,99999999)
        text = re.sub(r"Ä",   "Ae",text,99999999)
        text = re.sub(r"Ö",   "Oe",text,99999999)
        text = re.sub(r"Ü",   "Ue",text,99999999)
        text = re.sub(r"ß",   "ss",text,99999999)
        text = re.sub(r"a\"", "ae",text,99999999)
        text = re.sub(r"o\"", "oe",text,99999999)
        text = re.sub(r"u\"", "ue",text,99999999)
        text = re.sub(r"A\"", "Ae",text,99999999)
        text = re.sub(r"O\"", "Oe",text,99999999)
        text = re.sub(r"U\"", "Ue",text,99999999)
        text = re.sub(r"s\"", "ss",text,99999999)
        text = re.sub(r"&",   "u", text,99999999)
        text = re.sub(chr(13),"",  text,99999999)
        return(text)

#******************************************************************************

    def ukto_from_betrag (self,betrag):

        id = re.sub(r"^\-","",betrag)
        id = re.sub("\.","",id)
        id = re.sub(r"^(.*?0)0*$","\\1",id)
        id = id + "00000"  # damit immer 5 Zeichen lang
        id = id[0:min(5,len(id))]
        return(id)

#******************************************************************************

    def compute_teilmengen (self,elems,laenge,teilmengen=[set()]):

        '''
        Rechnet alle Teilmengen der Laenge laenge aus der Menge (0,1,...,elems) aus
        '''

        if type(elems) == type(0):
            anzahl = elems
            elems  = []
            while (0 == 0):
                anzahl = anzahl - 1
                elems.append(anzahl)
                if anzahl == 0:
                    break

        newteilmengen = []
        for teilmenge in teilmengen:
            for elem in elems:
                if len(teilmenge) == 0 or elem > max(teilmenge):
                    o = teilmenge.copy()
                    o.add(elem)
                    newteilmengen.append(o)
        if laenge == 1:
            return(newteilmengen)
        else:
            return( self.compute_teilmengen (elems,laenge-1,newteilmengen) )
    
#******************************************************************************

    def identify_betrkto (self,idem,betrktos,grenze,laenge=1):

#        print ("-->",idem,grenze,laenge)

        '''
        Diese Funktion bringt, wenn moeglich, Betragskonten zusammen, so dass sie
        auf Null aufgehen
        '''

        matchval = list(betrktos.keys())
#        print (laenge,betrktos)
        if len(matchval) > { 1:99999999, 2:1000, 3:120, 4:50, 5:30, 6:24, 7:20, 8:18, 9:17, 10:16,
                              11:15,  12:16, 13:15, 14:15, 15:15 } [laenge] or len(matchval) < laenge:
            return()
        if len(matchval) > { 1:99999999, 2:500, 3:80, 4:60, 5:25, 6:20, 7:15, 8:10, 9:9} [laenge]:
            return()
#        if len(matchval) > { 1:99999999, 2:300, 3:50, 4:30, 5:20, 6:15, 7:10, 8:8} [laenge]:
#            return()
        
        matchsets = self.compute_teilmengen(len(matchval),laenge)  #  die moeglichen Index-sets
        
        while (0 == 0):

            if matchsets == []:
                break
            matchset = matchsets.pop(0)            
            summe    = 0.00
            for i in matchset:
                summe = summe + betrktos[ matchval[i] ]
            
            if abs(summe) < grenze:

                refkto = []
                for i in matchset:
                    refkto.append( matchval[i] )
                    del betrktos[ matchval[i] ]
                refkto.sort()
                refkto = refkto[-1]
                
#                print ("----")
                
                for i in matchset:
#                    print (matchval[i] )
                    idem[ matchval[i] ] = refkto
#                print ("----")
                    
                matchsets1 = matchsets[:]    #  alle Indexe entfernen, die schon gematcht sind
                matchsets  = []
                for matchset1 in matchsets1:
                    matchset_weiter_untersuchen = True
                    for i in matchset:
                        if i in matchset1:
                            matchset_weiter_untersuchen = False
                            break
                    if matchset_weiter_untersuchen:
                        matchsets.append(matchset1)
                    
        self.identify_betrkto (idem,betrktos,grenze,laenge+1)                                

#********************************************************************************

    def idem_kto (self,ktodata,grenze=0.99):

        text      = []
        buchungen = []
        betrktos  = {}
          
        for zeile in ktodata['CONTENT'].split("\n"):

            m = re.search("^(\d\d\d\d\d) +(\-?\d+\.\d\d) *$",zeile)
            if m:
                betrkto = "-" + self.ukto_from_betrag(m.group(1))
                if betrkto in betrktos:
                    betrktos[betrkto] = betrktos[betrkto] + float(m.group(2))
                else:
                    betrktos[betrkto] = float(m.group(2))
                text.append(zeile)
                continue

            m = re.search(r"^(\d\d\d\d\d\d\d\d) +(\-?\d+\.\d\d) +(\S+) +(\S+) +(\-?\d+\.\d\d) +(.*)",zeile)
            if not m:
                text.append(zeile)
            else:
                datum   = m.group(1)
                betrag  = m.group(2)
                ktoa    = m.group(3)
                ktob    = m.group(4)
                remark  = m.group(6)
#                betrkto = "-" + self.ukto_from_betrag(betrag)
#                if betrkto in betrktos:
#                    betrktos[betrkto] = betrktos[betrkto] + float(m.group(2))
#                else:
#                    betrktos[betrkto] = float(m.group(2))
                buchungen.append([datum,betrag,ktoa,ktob,"0.00",remark])

#        print (betrktos)
#        exit()
        idem = {}
        self.identify_betrkto(idem,betrktos,grenze)

#        for o in idem:
#            if not o == idem[o]:
#                print (o,idem[o])

        for buchung in buchungen:
            if buchung[2] in idem and ("-" + self.ukto_from_betrag(buchung[1])) == buchung[2]:
                buchung[2] = idem[ buchung[2] ]
            text.append("  ".join(buchung))
            
#        print (ktodata['CONTENT'])

        ktodata['CONTENT'] = "\n".join(text) + "\n"
        

#********************************************************************************


if __name__ == "__main__":
    if sys.argv[2] in Account3.__dict__:
        Account3.__dict__[sys.argv[2]](Account3(sys.argv[1]),*sys.argv[3:])
    else:
        Account3.__dict__["cx"](Account3(sys.argv[1]),*sys.argv[2:])

bypass 1.0, Devloped By El Moujahidin (the source has been moved and devloped)
Email: contact@elmoujehidin.net bypass 1.0, Devloped By El Moujahidin (the source has been moved and devloped) Email: contact@elmoujehidin.net