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/kto.py

#  coding:  utf8

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

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

class Kto (object):

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

        sort_text  = "LOHN,ZAHL,LS,SZ,KI,AN,AR,PL,KV,RV,AV,PV,U1,U2,U3"
        try:
            import fibu
            sort_text = sort_text + "," + fibu.sort_text
        except:
            pass
            
        try:
            self.database_mode = fibu.db
        except:
            self.database_mode = ""

        self.sort_text = {}
        for o in sort_text.split(","):
            self.sort_text[o] = ( "%03u" % (len(self.sort_text)+1) )
        

    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 read_template (self,ktotext):

        m = re.search(r"\n(\d\d\d\d\d\d\d\d)( +\S+)( +)(\S*)( +)(\S+)( +\-?\d+\.\d\d)( +.*?)\n",ktotext)
        if m:
            self.maxa = max(14,len(m.group(3)) - 20)
            self.maxb = max(14,len(m.group(5)) - 20)
        m = re.search(r"\n( +\-?\d+\.\d\d) *\n",ktotext)
        if m:
            self.maxc = max(10,len(m.group(1)) - 15)

#        print(self.dir,"..1..",self.maxa,self.maxb,self.maxc)


    def parse_ktotext (self,ktotext,bezeichner):

        self.buchungen = []
        self.ktotext   = ktotext
        unique_strings = {}

        for zeile in self.ktotext.split("\n"):

            m = re.search(r"^(\d\d\d\d)(..)(\d\d) +(\S+) +(\S*) +(\S+) +(\-?\d+\.\d\d) +(.*)$",zeile)

#            print(m)
            if not m:   #   Einlesen der Kontobezeichnungen
                m1 = re.search(r"^(\S+) +(\-?\d+\.\d\d) +(.*?) *$",zeile)
                if m1:
                    bezeichner[ self.ukto + "-" + m1.group(1)] = m1.group(3)
                continue

            if m.group(2) == "MM":
                monate = ["01","02","03","04","05","06","07","08","09","10","11","12"]
            else:
                monate = [m.group(2)]

            for monat in monate:

                datum  = m.group(1) + monat + m.group(3)
                try:
                    betrag = "%3.2f" % eval(m.group(4))
                except:
                    print(m.group(4))
                    exit()

                remark = m.group(8)
                uniqu  = []

                ktoa = self.parse_ktotext_compute_kto(m.group(5),self.ukto,uniqu)
                ktob = self.parse_ktotext_compute_kto(m.group(6),self.ukto,uniqu)

                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))
                uniqu   = uniqu1

                betrag1 = re.sub("\-","",betrag) + ","
                remark1 = "," + re.sub(r"[\+\- ]","",remark,9999)
                uniqu   = betrag1 + ",".join(uniqu) + remark1
                if datum in unique_strings:  #  to avoid double entries
                    if uniqu in unique_strings[datum]:
                        continue  #  der Eintrag mit diesem unique-String existiert schon, daher nicht nochmal nehmen
                    else:
                        unique_strings[datum].append(uniqu)
                else:
                    unique_strings[datum] = [uniqu]

                self.buchungen.append([datum,betrag,ktoa,ktob,remark])


    def parse_ktotext_compute_kto (self,ktox,ukto,uniqu):      #   helper function to parse_ktotext

        if ktox == "-":
            ktox  = ukto
        elif ktox[0:1] == "-":
            if ukto == "":
                ktox = ktox[1:]
            else:
                ktox  = ukto + ktox
        elif ktox[0:len(ukto)] == ukto:
            pass
        else:
            uniqu.append(ktox)
            
        return(ktox)

        
    def import_to_db (self,dbh):

        if self.database_mode == "file":
            return(self.file_import_to_db(dbh))
            
        cursor = dbh.cursor()
        for buchung in self.buchungen:

             qstr = ("insert into buchungen (DATUM,BETRAG,KTOA,KTOB,REMARK,ID) values (" +
                         "'" + buchung[0] + "'," +
                         ("%3.2f" % float(buchung[1])) + "," +
                         "'" + buchung[2] + "'," +
                         "'" + buchung[3] + "'," +
                         "'" + buchung[4] + "',''" +
                     ")"  )
#             print(qstr)
             cursor.execute(qstr)


    def file_import_to_db (self,dbh):

        text = []
        for buchung in self.buchungen:
            text.append(buchung[0] + "  " + ("%13.2f" % float(buchung[1])) + "  -" + ("%-30s" % buchung[2]) + 
                        "  -" + ("%-30s" % buchung[3]) + "          0.00  " + buchung[4])
        open(dbh,"a").write("\n".join(text))

        
    def write_datev (self,berater,mandant):

        text           = {}
        bestandskonten = {}
        
        template_zeile = '72402,59;"S";"";;;"";9090;3035;"";3012;"";"";;"Berechnung Gewerbesteuer";;"";;;;"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";;"";;"";;;;;;"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";;;;"";;;;"";"";0;"";;;0;"RE";"xxxxxx";;"";0;"";"";"";"";;"";;0;;;;0;0,00;""'
        template_zeile = re.sub(r'"xxxxxx"','""',template_zeile)
        jahr0          = ""

        for zeile in self.ktotext.split("\n") + ["29990101  0.00  A  B  0.00  XXX"]:
        
            m = re.search(r"^(\d\d\d\d\d\d\d\d) +(\-?\d+\.\d\d) +(\S+?) +(\S+?) +(\-?\d+\.\d\d) +(.*)$",zeile)
            if not m:
                continue
                
            datum  = m.group(1)
            betrag = float(m.group(2)) + 0.000001
            ktoa   = m.group(3)
            ktob   = m.group(4)
            saldo  = float(m.group(5))
            remark = m.group(6)
            
            jahr   = datum[0:4]
            monat  = datum[4:6]
            tag    = datum[6:8]
            
            if int(tag) > 31:
                tag = "31"
            if int(tag) > 30 and monat in ("04","06","08","10","12"):
                tag = "30"
            if int(tag) > 28 and monat == "02":
                tag = "28"
            if int(tag) == 1 and monat == "01":
                tag = "02"

            if "v.H." in remark:
                continue

            zeile = template_zeile.split(";")
            
            if betrag > 0.0:
                zeile[1] = '"H"'
            else:
                zeile[1] = '"S"'
            if abs(betrag) < 0.002:
                continue
            zeile[0]  = ("%3.2f" % abs(betrag))
            zeile[0]  = re.sub(r"\.",",",zeile[0])
            zeile[6]  = re.sub(r"^(.*?)\-(\d\d\d\d)(\-.*|$)","\\2",ktob)
            zeile[7]  = re.sub(r"^(.*?)\-(\d\d\d\d)(\-.*|$)","\\2",ktoa)
            m         = re.search(r"([\+\-]*)(.*)$",remark)
            
            if zeile[6][0:2] == "44":
                zeile[6] = "4400"
            if zeile[7][0:2] == "44":
                zeile[7] = "4400"


            if not( zeile[6][0] in ("0123") and zeile[7][0] in ("0123") ):
                if not zeile[6][0:2] == "44" and not zeile[7][0:2] == "44":
#                    print(zeile[6],zeile[7])
                    if m.group(1) == "++":
                        zeile[8] = '"9"'
                    elif m.group(2) == "+-":
                        zeile[8] = '"8"'

            zeile[13] = m.group(2)
            zeile[13] = re.sub(r" +"," ",zeile[13],99999999)
            zeile[13] = zeile[13][0:60]
            zeile[9]  = tag + monat
            
            if zeile[6][0] in ("0123"):
                if not zeile[6] in bestandskonten:
                    bestandskonten[zeile[6]] = 0.00
            if zeile[7][0] in ("0123"):
                if not zeile[7] in bestandskonten:
                    bestandskonten[zeile[7]] = 0.00

            for kto in bestandskonten:
                if zeile[6] == kto:
                    bestandskonten[kto] = bestandskonten[kto] + betrag
                if zeile[7] == kto:
                    bestandskonten[kto] = bestandskonten[kto] - betrag

            if not jahr in text:
                text[jahr] = [ '"DTVF";700;21;"Buchungsstapel";9;20190122155620609;;"SV";"Gbl";"";15312;11339;'+jahr+'0101;4;'+jahr+'0101;'+jahr+'1231;"EBS-Buchungsstapel";"";1;0;0;"EUR";;"MP";;211678;"04";;;"";""' + "\n" +
                               'Umsatz (ohne Soll/Haben-Kz);Soll/Haben-Kennzeichen;WKZ Umsatz;Kurs;Basis-Umsatz;WKZ Basis-Umsatz;Konto;Gegenkonto (ohne BU-Schluessel);BU-Schluessel;Belegdatum;Belegfeld 1;Belegfeld 2;Skonto;Buchungstext;Postensperre;Diverse Adressnummer;Geschaeftspartnerbank;Sachverhalt;Zinssperre;Beleglink;Beleginfo - Art 1;Beleginfo - Inhalt 1;Beleginfo - Art 2;Beleginfo - Inhalt 2;Beleginfo - Art 3;Beleginfo - Inhalt 3;Beleginfo - Art 4;Beleginfo - Inhalt 4;Beleginfo - Art 5;Beleginfo - Inhalt 5;Beleginfo - Art 6;Beleginfo - Inhalt 6;Beleginfo - Art 7;Beleginfo - Inhalt 7;Beleginfo - Art 8;Beleginfo - Inhalt 8;KOST1 - Kostenstelle;KOST2 - Kostenstelle;Kost-Menge;EU-Land u. UStID;EU-Steuersatz;Abw. Versteuerungsart;Sachverhalt L+L;Funktionsergaenzung L+L;BU 49 Hauptfunktionstyp;BU 49 Hauptfunktionsnummer;BU 49 Funktionsergaenzung;Zusatzinformation - Art 1;Zusatzinformation- Inhalt 1;Zusatzinformation - Art 2;Zusatzinformation- Inhalt 2;Zusatzinformation - Art 3;Zusatzinformation- Inhalt 3;Zusatzinformation - Art 4;Zusatzinformation- Inhalt 4;Zusatzinformation - Art 5;Zusatzinformation- Inhalt 5;Zusatzinformation - Art 6;Zusatzinformation- Inhalt 6;Zusatzinformation - Art 7;Zusatzinformation- Inhalt 7;Zusatzinformation - Art 8;Zusatzinformation- Inhalt 8;Zusatzinformation - Art 9;Zusatzinformation- Inhalt 9;Zusatzinformation - Art 10;Zusatzinformation- Inhalt 10;Zusatzinformation - Art 11;Zusatzinformation- Inhalt 11;Zusatzinformation - Art 12;Zusatzinformation- Inhalt 12;Zusatzinformation - Art 13;Zusatzinformation- Inhalt 13;Zusatzinformation - Art 14;Zusatzinformation- Inhalt 14;Zusatzinformation - Art 15;Zusatzinformation- Inhalt 15;Zusatzinformation - Art 16;Zusatzinformation- Inhalt 16;Zusatzinformation - Art 17;Zusatzinformation- Inhalt 17;Zusatzinformation - Art 18;Zusatzinformation- Inhalt 18;Zusatzinformation - Art 19;Zusatzinformation- Inhalt 19;Zusatzinformation - Art 20;Zusatzinformation- Inhalt 20;Stueck;Gewicht;Zahlweise;Forderungsart;Veranlagungsjahr;Zugeordnete Faelligkeit;Skontotyp;Auftragsnummer;Buchungstyp;USt-Schluessel (Anzahlungen);EU-Land (Anzahlungen);Sachverhalt L+L (Anzahlungen);EU-Steuersatz (Anzahlungen);Erloeskonto (Anzahlungen);Herkunft-Kz;Buchungs GUID;KOST-Datum;SEPA-Mandatsreferenz;Skontosperre;Gesellschaftername;Beteiligtennummer;Identifikationsnummer;Zeichnernummer;Postensperre bis;Bezeichnung SoBil-Sachverhalt;Kennzeichen SoBil-Buchung;Festschreibung;Leistungsdatum;Datum Zuord. Steuerperiode;Faelligkeit;Generalumkehr (GU);Steuersatz;Land' ]
                mandant1 = re.sub(r"JJ",jahr[2:4],mandant)
                text[jahr][0] = re.sub(r"\;15312;",";"+str(berater) +";",text[jahr][0])
                text[jahr][0] = re.sub(r"\;11339;",";"+str(mandant1)+";",text[jahr][0])
                
                if jahr0:
                    
                    kontenkeys = list(bestandskonten.keys())
                    kontenkeys.sort()
                    kontenkeys.reverse()
                    for kto in kontenkeys:
                        betrag   = abs(bestandskonten[kto])
                        zeile    = template_zeile.split(";")
                        zeile[6] = kto
                        if betrag > 0.0:
                            zeile[1] = '"H"'
                            zeile[7] = "9000"
                        else:
                            zeile[1] = '"S"'
                            zeile[7] = "9008"
                        if abs(betrag) < 0.002:
                            continue
                        zeile[0]  = ("%3.2f" % abs(betrag))
                        zeile[0]  = re.sub(r"\.",",",zeile[0])
                        zeile[13] = "Saldovortrag"
                        zeile[9]  = "0101"
                        text[jahr0].insert(1,";".join(zeile))
                        
            text[jahr].append(";".join(zeile))
            jahr0 = jahr
            
        text1 = {}
        for jahr in text.keys():
            if int(jahr) < 2900:
                text1[jahr] = "\n".join(text[jahr]) + "\n"
        return(text1)                


    def delete_from_db (self,dbh):
    
        if self.database_mode == "file":
            return(self.file_delete_from_db(dbh))
            
        cursor = dbh.cursor()
        qstr   = ("delete from buchungen where (KTOA like '" +    
                   self.ukto + "%' OR " + " KTOB like '" + self.ukto + "%') " +
                   " and DATUM >= '" + self.startdatum + "' and DATUM <= '" + self.enddatum + "' ")
        cursor.execute(qstr)

    def file_delete_from_db (self,dbh):
    
        text = []
        for line in open(dbh):
            if self.ukto in line:
                m = re.search("^(\d\d\d\d\d\d\d\d) +(\-?\d+\.\d\d) +\-(\S+) +\-(\S+)",line)
                if not ( (m.group(3).startswith(self.ukto) or m.group(4).startswith(self.ukto)) and
                         (self.startdatum <= m.group(1) <= self.enddatum) ):
                    text.append(line)
            else:
                text.append(line)
        open(dbh,"w").write("".join(text))


    def export_from_db (self,dbh):
    
        if self.database_mode == "file":
            return(self.file_export_from_db(dbh))
            
        cursor = dbh.cursor()
        qstr   =        " select DATUM,BETRAG,KTOA,KTOB,REMARK,ID 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,KTOA,KTOB,REMARK,BETRAG" 
#        print(qstr)
        cursor.execute(qstr)
        
        buchungen  = []
        while (0 == 0):
            entry = cursor.fetchone()
            if not entry:
                break
            buchungen.append([entry[0],entry[1],entry[2],entry[3],entry[4]])
                        
        return(buchungen)

    def file_export_from_db (self,dbh):
    
        buchungen = []
        print("UKTO",self.ukto,self.startdatum,self.enddatum)
        for line in open(dbh):
            if self.ukto in line:
#                print(line)
                m = re.search("^(\d\d\d\d\d\d\d\d) +(\-?\d+\.\d\d) +\-(\S+) +\-(\S+) +(\d+\.\d\d) +(.*)\s*$",line)
#                if m:
#                    print(m.group(1),m.group(3),m.group(4))
                if m and ( (m.group(3).startswith(self.ukto) or m.group(4).startswith(self.ukto)) and
                            self.startdatum <= m.group(1) <= self.enddatum):
                    buchungen.append([m.group(1),float(m.group(2)),m.group(3),m.group(4),m.group(6)])
#                    print(line)
#            return()
        return(buchungen)

    def db_id (self,buchungen):
    
        text = []
        for buchung in buchungen:
            text.append("|".join([buchung[0],str(buchung[1]),buchung[2],buchung[3],buchung[4]]))
        id = str( base64.urlsafe_b64encode(hashlib.md5( ("\n".join(text)).encode("utf-8")).digest()),"ascii" )
        return(id[0:6])

    def saldo_from_db (self,dbh,before_date=None):
    
        if not before_date:
            before_date = self.startdatum

        cursor = dbh.cursor()
        qstr   =        " select sum(BETRAG) from buchungen "
        qstr   = qstr + " where (KTOA like '" + self.ukto + "%')"
        qstr   = qstr + " and DATUM < '" + before_date + "'"
        cursor.execute(qstr)
        
        betrag = cursor.fetchone()[0]
        
        cursor = dbh.cursor()
        qstr   =        " select sum(BETRAG) from buchungen "
        qstr   = qstr + " where (KTOB like '" + self.ukto + "%')"
        qstr   = qstr + " and DATUM < '" + before_date + "'"
        cursor.execute(qstr)
        
        betrag = betrag - cursor.fetchone()[0]
        
#********************************************************************************

    def write_ktotext (self,bezeichner,sortfkt=None,saldovortrag=0.00):

        ul     = len(self.ukto)
        salden = {}

        text_intern = []
        text_extern = []
        
        maxa = 0
        maxb = 0

        for buchung in self.buchungen:    #  Zeilen des Kontos schreiben
           
            datum     = buchung[0]
            betrag    = float(buchung[1])
            ktoa      = buchung[2]
            ktob      = buchung[3]
            remark    = buchung[4]

            anz       = 0
            if ktoa[0:ul] == self.ukto:
                ktoa = ktoa[ul:]
                if ul == 0:
                    ktoa = "-" + ktoa
                self.compute_salden(salden,ktoa,betrag,datum)
                if ktoa == "":
                    ktoa = "-"
                anz = anz + 1
#            elif ul == 0:
#                ktoa = ktoa[1:]

            if ktob[0:ul] == self.ukto:
                ktob = ktob[ul:]
                if ul == 0:
                    ktob = "-" + ktob
                self.compute_salden(salden,ktob,-betrag,datum)
                if ktob == "":
                    ktob = "-"
                if anz == 0:
                    o      = ktoa
                    ktoa   = ktob
                    ktob   = o
                    betrag = -betrag
                anz = anz + 1
#            elif ul == 0:
#                ktob = ktob[1:]
         
            maxa = max(maxa,len(ktoa))
            maxb = max(maxb,len(ktob))
            
            if anz == 1:
                pass
            
            
            if re.search(r"^\d\d? +v\.?H\.? ",remark):   #  USt-Einrueckung
                remark = "    " + remark
            buchung1    = ["",datum," "+("%13.2f" % betrag),ktoa,ktob,"  "+remark,anz]
            rem         = re.sub(r"^[\+\-]+","",remark)   #  um die Umsatzsteuerbuchungen genau hinter die entsprechnden
            m = re.search(r"^ +(.*?)\((.*)\)",rem)        #  Buchungen zu bekommen
            if m:
                rem = m.group(2)  + ("Z" * 10)
            else:
                rem = rem + ("A" * 10)
            buchung1[0] = self.mask_texts(buchung1[1]+buchung1[3]+rem)
#            print(buchung1[0])

            if anz == 1:
                text_extern.append(buchung1)
            else:
                text_intern.append(buchung1)


        text_extern.sort(key=lambda x: x[0])
        text_intern.sort(key=lambda x: x[0])
        maxa1 = "%-" + ("%1u" % maxa) + "s"
        maxb1 = "%-" + ("%1u" % maxb) + "s"

        gesamt       = saldovortrag
        text_extern1 = []
        for zeile in text_extern:
            gesamt = gesamt + float(zeile[2])
            ktoa   = "  " + (maxa1 % zeile[3])
            ktob   = "  " + (maxb1 % zeile[4])
            text_extern1.append( zeile[1]+zeile[2]+ktoa+ktob+" " + ("%13.2f"%gesamt) + zeile[5] )
        text_intern1 = []
        for zeile in text_intern:
            ktoa   = "  " + (maxa1 % zeile[3])
            ktob   = "  " + (maxb1 % zeile[4])
            text_intern1.append( zeile[1]+zeile[2]+ktoa+ktob+" " + ("%13.2f"%gesamt) + zeile[5] )
            
#        self.mark("Start Salden")
        
        if not "" in salden:
            salden[""] = { "XX": 0.00 }

        ktoliste0   = list(salden.keys())
        ktoliste0.sort()
        ktoliste    = []
        for kto in ktoliste0:
            ktoliste.append([ self.mask_texts(kto) , kto ])
        ktoliste.sort(key=lambda x: x[0])
               
        maxc1  = 1
        maxord = 0
        for kto0 in ktoliste:
            kto     = kto0[1]
            maxc1   = max(len(kto),maxc1)
            maxord  = max(len(re.sub("[^\-]","",kto)),maxord)
        maxc1  = "%-" + ("%1u" % maxc1) + "s"
        maxord = "  " + ("    " * 4)

        kto2 = ""
        for kto0 in ktoliste:
            kto         = kto0[1]
            kto_ordnung = max(1,len(re.sub("[^\-]","",kto)))
            kto_ordnung = "    " * kto_ordnung
            kto1        = re.sub(r"^\-*","",kto)
            zeile       = (maxc1 % kto1 ) + kto_ordnung  + ("%13.2f" % sum(salden[kto].values()))
            
            if kto1 == "":
                text_salden = [zeile[4:],""]
            else:            
                if "-" in kto2 and not "-" in kto1:
                    text_salden.append("")
                
                zeile = (maxc1 % kto1) + kto_ordnung  + ("%13.2f" % sum(salden[kto].values()))
                id    = self.ukto + "-" + kto1
                if id in bezeichner:
                    zeile = zeile + maxord[len(kto_ordnung):] + bezeichner[id]  #  formatting
                text_salden.append( zeile )

            kto2 = kto1

        self.ktotext = "\n".join(text_extern1) + "\n\n" + "\n".join(text_salden) + "\n\n" + "\n".join(text_intern1) + "\n"
        return(gesamt)

    def write_csv (self):
    
        csv = [] # filename,""]
        
        for zeile in self.ktotext.split("\n"):
        
            m = re.search(r"^(\d\d\d\d\d\d\d\d) +(\-?\d+\.\d\d) +(\S+?) +(\S+?) +(\-?\d+\.\d\d) +(.*)$",zeile)
            if not m:
                m = re.search(r"^(\S+?) +(\S+?) *$",zeile)
                if m:
                    csv.append(m.group(1)+";"+m.group(2))
                else:
                    m = re.search(r"^(\S+?) +(\S+?) +(\S+?) *$",zeile)
                    if m:
                        csv.append(m.group(1)+";"+m.group(2)+";"+m.group(3))
                    else:
                        csv.append(zeile)
                continue
                
            datum  = m.group(1)
            betrag = float(m.group(2))
            ktoa   = m.group(3)
            ktob   = m.group(4)
            saldo  = float(m.group(5))
            remark = m.group(6)
            
            jahr   = datum[0:4]
            monat  = datum[4:6]
            tag    = datum[6:8]
            
            if int(tag) > 31:
                tag = "31"
            if int(tag) > 30 and monat in ("04","06","08","10","12"):
                tag = "30"
            if int(tag) > 28 and monat == "02":
                tag = "28"

            ktoa = [ktoa]
            ktob = [ktob]

#            ktoa = (ktoa+"----").split("-")
#            ktoa = ktoa[0:5]+[ re.sub(r"\-+$","","-".join(ktoa[5:99])) ]
#            ktob = (ktob+"----").split("-")
#            ktob = ktob[0:5]+[ re.sub(r"\-+$","","-".join(ktob[5:99])) ]

            zeile1 = (   [ datum[6:8] + "." + datum[4:6] + "." + datum[0:4],
                           re.sub(r"\.",",",("%3.2f" % betrag)) ]  # , "Unterkonto:" ]
                       + ktoa # + [ "Gegenkonto:" ]
                       + ktob + [
                       re.sub(r"\.",",",("%3.2f" % saldo)), remark ] )

            csv.append(";".join(zeile1))
            
        csv = "\n".join(csv)+"\n"
            
        return(csv)

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

    def mask_texts (self,text):
    
        for o in self.sort_text:
            text = text.replace(o,self.sort_text[o])
        return(text)


    def xxcompute_salden (self,salden,ukto,betrag):

        while (0 == 0):
            m = re.search(r"^(.*)\-(.+)$",ukto)
            if m:
                if not ukto in salden:
                    salden[ukto] = 0.0
                salden[ukto] = salden[ukto] + betrag
                ukto = m.group(1)
            else:
                return()

    def compute_salden (self,salden,ukto,betrag,datum):

        monat = datum[4:6]
        monat = "XX"

        while (0 == 0):
            if not ukto in salden:
                salden[ukto] = {}
            if not monat in salden[ukto]:
                salden[ukto][monat] = 0.00
            salden[ukto][monat] = salden[ukto][monat] + betrag

            m = re.search(r"^(.*)\-(.+)$",ukto)
#            if not m:
#                m = re.search(r"^(.*?)(.+)$",ukto)
            if m:
                ukto = m.group(1)
            else:
                return()

    def find_ukto_and_interval (self,ktofile,rules_dir=None):
    
        filename   = []
#        print(rules_dir)
        if rules_dir == None:
            import fibu
            self.rules_dir = re.sub(r"(.*[\\\/]).*","\\1",fibu.__file__)
            try:
                filename = [fibu.name]
            except:
                pass
        else:
            self.rules_dir = rules_dir
            
        self.ukto_dir = re.sub(r"\\","/",os.path.relpath(ktofile,self.rules_dir),9999)
        self.ukto_dir = re.sub(r"([\\\/\-]\d\d\d\d)\-([123456789ABC][\\\/]?$)","\\1/\\2",self.ukto_dir)
#        print(self.rules_dir,self.ukto_dir)

        ukto        = []
        interval    = ""
        self.month  = "" 
        bezeichnung = ""

        for partdir in self.ukto_dir.split("/"):
            bezeichnung = ""
            m = re.search(r"^(.*?)(\_\_.*)$",partdir)
            if m:
                ktopart     = m.group(1)
                bezeichnung = m.group(2)
            else:
                ktopart = partdir
            m = re.search(r"^(|.*\-)([123456789ABCIJKLMNP])$",ktopart)
            if m:
                self.month = m.group(2)
                ktopart    = m.group(1)
                if ktopart == "":
                    continue
                else:
                    ktopart = ktopart[:-1]
#                print("www",self.month,ktopart)
            
            m = re.search(r"^(.*)(\_)$",ktopart)
            if m:
                ktopart     = "Z999"
                bezeichnung = "__" + m.group(1)
            m = re.search(r"^(Z\d\d\d|Z\d\d\d\d\d|[Z\d]\d\d\d\d*\_[Z\d]\d\d\d\d*)$",ktopart)
            if m:
                interval = "2" + m.group(1)[1:]
            else:
                ukto.append(ktopart)
            if not ktopart == ".":
                filename.append(ktopart)

            if interval == "2999":
                interval = "00000000_99999999"
                
            m = re.search(r"^(.*)\_(.*)$",interval)
            if m and not len(interval) == len("00000000_99999999"):
                interval = (m.group(1) + "00000000")[0:8] + "_" + (m.group(2) + "99999999")[0:8]

        ukto = "-".join(ukto)
        ukto = ukto.split("-")
        
#        print(ukto)
#        print("---",interval,self.month)

        if not self.month == "":  #   re.search(r"^[123456789ABCIJKLMNP]$",ukto[-1]):
#            self.month = ukto.pop()
            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"] } [ self.month ]
            if len(interval) > 3:
                interval = interval[0:4] + intv[0] + "_" + interval[0:4] + intv[1]
            
        if re.search(r"^(\d\d\d\d|\d\d\d\d\d\d|\d\d\d\d\d*\_\d\d\d\d\d*)$",ukto[0]):
            interval = ukto.pop(0)

        self.ukto         = re.sub(r"\.","",   "-".join(ukto)   )
        self.interval     = interval
        self.filename     = "-".join(filename) + bezeichnung
        self.bezeichnung  = re.sub("^\_+","",bezeichnung)
        self.compute_start_and_end()
        

    def compute_start_and_end (self):
    
        interval = self.interval
        
        if len(interval) == 0:
            return(0)
            
        if re.search(r"^\d\d\d\d$",interval):
            self.startdatum = interval + "0000"
            self.enddatum   = interval + "9999"
        elif re.search(r"^\d\d\d\d\d\d$",interval):
            self.startdatum = interval + "00"
            self.enddatum   = interval + "99"
        else:
            m = re.search(r"^(\d\d\d\d\d\d\d\d)\_(\d\d\d\d\d\d\d\d)$",interval)
            if m:
                self.startdatum = m.group(1)
                self.enddatum   = m.group(2)
                

    def kto_id (self,ktodir):

        ktofilename = ""

        if type(ktodir) == type(1):
            addtext = "%1u" % ktodir
        else:
            m = re.search(r"^(.*)\.kto$",glob.glob(ktodir+"/*")[0])
            if m:
                ktofilename = m.group(1)
            ktofiles = glob.glob(ktodir+"/*")
            if os.path.isfile(ktodir+"/subdirs1"):
                ktofiles = ktofiles + glob.glob(ktodir+"*/*")
            if os.path.isfile(ktodir+"/subdirs2"):
                ktofiles = ktofiles + glob.glo2(ktodir+"*/*/*")
            if os.path.isfile(ktodir+"/subdirs3"):
                ktofiles = ktofiles + glob.glob(ktodir+"*/*/*/*")
            ktofiles.sort()
            addtext = ""

            for addfile in ktofiles:

                if addfile == ktofilename + ".csv":
                    continue

                if not os.path.isfile(addfile):
                    continue
                if addfile[-1] == "~":
                    continue

                if re.search(r"\.(kto[\.\d]+|kto\_\_|py|pyc|db|elfo|db-journal|pdf.*|aux|dvi|gif|id|jpg|zip|xls|xlsx|doc|docx|\.\d+)$",
                            addfile) or "EXCLUDE" in addfile or "NOTVALID" in addfile or "~" in addfile:
                    if not "rule.py" in addfile:
                        continue

                m = re.search(r"^(.*)[\\\/](.*)$",addfile)
                if m:
                    ktofile = m.group(2)
                
                if not "." in m.group(2):
                    continue

#                print(addfile)
                try:
                    text = open(addfile,'r').read()
                except:
                    print(addfile + " could not be read.")
                if not re.search(r"\.kto$",ktofile):
                    addtext = addtext + ktofile
                else:
                    text = re.sub(r"\n====  .*$","",text,flags=re.DOTALL)
                    text = re.sub(r"^([^\n]*)\([A-Za-z0-9\+\-\_]{12}(\.?)\)[^\n]*","\\1",text,flags=re.DOTALL)

                if os.path.isfile(addfile):
                    addtext = addtext + str( base64.urlsafe_b64encode(hashlib.md5(text.encode("utf8")).digest()),"ascii" )

        addtext = str( base64.urlsafe_b64encode( hashlib.md5(addtext.encode("utf8")).digest()),"ascii" )
        addtext = re.sub(r"[^A-Za-z0-9]","",addtext+"xyzxyzxyz",99)

        return(addtext[0:6])
    

    def xxdb_id (self,ktodir):

        if type(ktodir) == type(1):
            addtext = "%1u" % ktodir
        else:

            ktofiles = glob.glob(ktodir+"/*")
            ktofiles.sort()
            addtext  = ""

            for addfile in ktofiles:

                if not os.path.isfile(addfile):
                    continue
                if re.search(r"\.(kto[\.\d]+|kto\_\_|py|pyc|db|elfo|db-journal|pdf.*|aux|dvi|gif|id|jpg|zip|xls|xlsx|doc|docx|\.\d+)$",
                            addfile) or "EXCLUDE" in addfile or "NOTVALID" in addfile or "~" in addfile:
                    if not "rule.py" in addfile:
                        continue

                m = re.search(r"^(.*)[\\\/](.*)$",addfile)
                if m:
                    ktofile = m.group(2)
                
                if not "." in m.group(2):
                    continue
  
                text = open(addfile,'r').read()
                if not re.search(r"\.kto$",ktofile):
                    addtext = addtext + ktofile
                else:
                    text    = re.sub(r"\n====  .*$","",text,flags=re.DOTALL)

                if os.path.isfile(addfile):
                    addtext = addtext + str( base64.urlsafe_b64encode(hashlib.md5(text.encode("utf8")).digest()),"ascii" )

        addtext = str( base64.urlsafe_b64encode( hashlib.md5(addtext.encode("utf8")).digest()),"ascii" )
        addtext = re.sub(r"[^A-Za-z0-9]","",addtext+"xyzxyzxyz",99)

        return(addtext[0:6])
    

    def xxassign_ausgaben (self,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)
        
        self.mark("W1")
        for zeile in self.ktotext.split("\n"):
            if '#' not in zeile:
                continue
            addinfo = []
            while (0 == 0):
                m = re.search(r"^(.*?)\#(.*?)\#(.*)$",zeile)
                addinfo.append(m.group(2))
                zeile = m.group(3)
            idbuch.append( ".*".join(addinfo) )
            print(idbuch[-1])
        self.mark("W2")

        zeilennr = 0
        for zeile in self.ktotext.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'] )

        self.ktotext = "\n".join(zeilen)

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

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

class Beleg (object):

    def __init__ (self,beleg):
    
        self.beleg = beleg


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

    def einlesen (self,ktodata,gegenkonto):
    
        belege      = ( glob.glob(ktodata['KTODIR']+"/*.ocr")  )
        buchungen   = []
        new_addtext = False

        for beleg in belege:

            text = open(beleg).read()

            attempt = 0       #   wenn die Datei nicht im Format JJMMDD.ustremark_betrag.ocr ist, dieses
            while (0 == 0):   #   erst versuchen zu erreichen

                attempt = attempt + 1
                m = re.search(r"/(\d\d\d\d\d\d\d?\d?)\.(q?[qw]?)\_?(.*?)\_(\d+)\_(\d\d)","/"+beleg)
                if m:
                    datum   = m.group(1)
                    if len(datum) == 6:
                        datum = "20" + datum
                    ust     = m.group(2)
                    remark  = m.group(3)
                    betrag  = "%3.2f" % float(m.group(4)+"."+m.group(5))
                    if not "minus" in remark:
                        betrag = "-" + betrag
                    betrag = re.sub(r"^--","",betrag)
                    buchungen.append([datum,betrag,"-",gegenkonto,"0.00",remark])
                    break
                
                else:
                    if attempt == 1:
                        erg = self.analyze(beleg)
                        if not erg:
                            print (beleg, "could not be parsed ...")
                        else:
                            datum1 = erg[2]
                            ust    = erg[1]
                            betrag = erg[0]
                            m      = re.search(r"^(\d+)\.(\d\d)$","%3.2f" % float(betrag))
                            newname_remark = re.sub(r"\_+$","",self.remark.keys()[0])
                            if newname_remark == "":
                                newname_remark = ( random.choice("abcdefghijkmnpqrstuvwxyz") +
                                                   random.choice("abcdefghijkmnpqrstuvwxyz") )
                            newname = ( datum1 + "." + ust + newname_remark + "_" + m.group(1) + "_" + m.group(2))
                            print (newname, "<--------------")
                            m       = re.search(r"^(.*)[\\\/](.*)\.pdf$",beleg)
                            if m:
                                newname = m.group(1) + "/" + newname
                                oldname = m.group(1) + "/" + m.group(2)
#                                print newname,oldname
                                if os.path.isfile(newname+".ocr") or os.path.isfile(newname+".pdf"):
                                    pass
                                else:
                                    new_addtext = True

                                    if os.path.isfile(oldname+".pdf"):
                                        os.rename(oldname+".pdf",newname+".pdf")
                                    if os.path.isfile(oldname+".ocr"):
                                        os.rename(oldname+".ocr",newname+".ocr")
                    
                                beleg = newname + ".pdf"
                    else:
                        break
                
        if new_addtext:
            self.fibu.make_addtext(ktodata)

        text = []
        for buchung in buchungen:
            text.append("  ".join(buchung))

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

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

    def analyze (self,beleg):
    
        belegfile = re.sub(r"^(.*)\.pdf$","\\1",beleg)
        try:
            text = open(belegfile+".ocr").read()
        except:
            return()
            
        m = re.search(r"^(.*)[\\\/](.*)$",belegfile)
        if m:
            filename = m.group(2)
        else:
            filename = ""

        text = re.sub(r"(\d+)\.(\d\d\d),(\d\d)","\\1\\2,\\3",text,9999)

        self.betraege = {}
        self.stunden  = {}
        self.stsatz   = {}
        self.mitarb   = {}
        self.remark   = { "xx" : 1 }
        self.text     = text
        
        while (0 == 0):    #   Ermittlung der Stunden
            m = re.search(r"^(.*?\D)(\d[\d+\.]*) +Stunden (.*)$",text,re.DOTALL)
            if not m:
                break
            m1     = re.search(r"\D([\d\.\,]+)\D",m.group(3)[0:40])
            stsatz = ""
            if m1:
                stsatz = re.sub(r",",".",m1.group(1))
            self.stunden[m.group(2)+" / "+stsatz] = 1
            text = m.group(1) + m.group(3)

#        print belegfile,filename
        m = re.search(r"[\\\/](\d?\d?)(\d\d\d\d\d\d)\_?(\D+)\_?","/"+filename)     #  Ermittlung des Datums
        if m:
            self.datum  = { m.group(2) : 1 }
            self.remark = { re.sub(r"\.","",m.group(3)) : 1 }
        else:
            m = re.search(r"[\\\/](\d\d)(\d\d)(\d\d)\.([a-z]+)\_(\D+)\_?","/"+filename)
            if m:
                self.datum  = { "20" + m.group(3) + m.group(2) + m.group(1) : 1 }
                self.mitarb = { m.group(4) : 1 }
                self.remark = { m.group(5) : 1 }
        if not m:
            self.datum = {}
            while (0 == 0):
                m = re.search(r"^(.*?[^\.\,0123456789])(\d\d?)[\.\-](\d\d?)[\.\-](\d?\d?)(\d\d)([^\.\,0123456789].*)$",text,re.DOTALL)
                if not m:
                    break
                self.datum[m.group(5)+("%02u"%int(m.group(3)))+("%02u"%int(m.group(2)))] = 1
                text = m.group(1) + m.group(6)

        while (0 == 0):    #  Ermittlung aller Betraege bzw. alles dessen, was nur annaehernd wie ein Betrag aussieht
            m = re.search(r"^(.*?[^\.\,0123456789])(\d+)[\,\.](\d\d)([^\.\,0123456789].*)$",text,re.DOTALL)
            if not m:
                break
            betrag = m.group(2)+"."+m.group(3)
            if float(betrag) > 0.00001:
                self.betraege[betrag] = {"b":1}
            text = m.group(1) + m.group(4)

        while (0 == 0):
            m = re.search(r"^(.*?)(Stundenaufstellung[^\n]*?, +|Name: +)(\S+).*? (\S+)(\D.*)$",text,re.DOTALL)
            if not m:
                break
            o = (m.group(3)[0] + m.group(4)).lower()
            o = re.sub("oime","onne",o)
            o = re.sub(r"^ss",m.group(3)[0:2]+"s",o)
            self.mitarb[o] = 1
            text = m.group(1) + m.group(5)

        for betrag in self.betraege:
            for betrag1 in self.betraege:
                if   "%3.2f" % (float(betrag) / float(betrag1)) == "1.07":
                    self.betraege[betrag]["m"] = 1
                elif "%3.2f" % (float(betrag) / float(betrag1)) == "1.19":
                    self.betraege[betrag]["n"] = 1
                elif "%3.2f" % (float(betrag1) / float(betrag) * 1.07) == "0.07":
                    self.betraege[betrag]["v"] = 1
                elif "%3.2f" % (float(betrag1) / float(betrag) * 1.19) == "0.19":
                    self.betraege[betrag]["u"] = 1
        
#        print self.betraege
#        print self.datum
#        print self.remark
#        print self.stunden
#        print self.mitarb

        ust    = ""
        erg    = {}
        datum1 = None
        for betrag in self.betraege:
            id = list(self.betraege[betrag].keys())
            id.sort()
            if "".join(id) in ("bnu","bu"):
                erg[betrag] = "qq"
            elif "".join(id) in ("bmv","bv"):
                erg[betrag] = "qw"
            elif len(self.betraege) == 1:
                erg[list(self.betraege.keys())[0]] = ""

#        print self.betraege
        ergs = list(erg.keys())
        ergs.sort(key=lambda x: float(x))
        if ergs:        
            ust = erg[ ergs[-1] ]
            erg = ergs[-1]
        else:
            erg = None
            
        if len(self.datum) > 0:
            self.datum = list(self.datum.keys())
            self.datum.sort()
            self.datum.reverse()
            datum1 = self.datum[0]
#            m = re.search(r"^(\d\d?)\.(\d\d?)\.(\d\d)(\d\d)",self.datum[0])
#            if m:
#                datum1 = m.group(4) + ("%02u" % int(m.group(2))) + ("%02u" % int(m.group(1)))

#        print self.datum, datum1, erg
        if datum1 and erg:
            return([erg,ust,datum1])
        return(None)

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

    def rechnungen (self,ktodata,company):

        ktodata['RULES_APPLY'] = 0
        if ktodata['RULES_APPLIED'] == True:
            self.fibu.idem_kto(ktodata,0.99)
            return()

        text      = []
        buchungen = {}
        betrktos  = {}
        
        for rechn in (glob.glob(ktodata['KTODIR'] + "/*.pdf")):

            if "nterschrieben" in rechn or "ndenzettel" in rechn or "NOTVALID" in rechn or "EXCLUDE§" in rechn:
                continue
            
            self.stunden  = None
            self.betraege = None
            self.mitarb   = None
            erg           = self.analyze(rechn)
            if "GULP" in self.text or "rogressive" in self.text:
                self.text = re.sub(r"HAYS","",self.text,9999,re.IGNORECASE)
            
#            m = re.search(r"[\n \_]" + company + "[ \_]",rechn+self.text,re.IGNORECASE)
#            if not m:
#                continue
            
#            print (1234)
            if erg:
                datum1 = erg[2]
                ust    = re.sub(r"q","+",re.sub(r"w","-",erg[1],9),9)
                betrag = erg[0]
                proj   = ""
                if self.mitarb:
                    o = list(self.mitarb.keys())[0]
                else:
                    o = "unknown"
                if self.stunden:
                    o = o + " " + list(self.stunden.keys())[0]
#                    m = re.search(r"\/ *(.*?)(\.|$)",self.stunden.keys()[0])
#                    if m:
#                        proj = "-" + m.group(1)
                m  = re.search(r"^(.*)[\\\/](.*)\.pdf$",rechn)
                if m:
                    o = o + ", " + m.group(2)
                if int(datum1[4:6]) > 31:
                    datum1 = datum1[0:4] + "30"
                
                betrkto = "-" + self.fibu.ukto_from_betrag(betrag)
                buchungen["20"+datum1[0:4]+betrkto+"12"] = (
                   ["20"+datum1,betrag,betrkto,"12-8400-"+company,"0.00",ust+o+", "+company])

        text = []     #   ---- Doppelbuchungen ausschliessen
        for zeile in ktodata['CONTENT'].split("\n"):

            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)
                if True or not (datum[0:6]+betrkto+ktob[0:2]) in buchungen:   #   das muss mal ueberprueft werden !!
                    if re.search(r"^-\d\d\d\d\d$",ktoa) or ktoa == "-":
                        ktoa = "-" + self.fibu.ukto_from_betrag(betrag)
                    buchungen[datum+betrkto+ktob+remark] = [datum,betrag,ktoa,ktob,"0.00",remark]

        for buchung in buchungen.values():
            text.append("  ".join(buchung))
        ktodata['CONTENT'] = "\n".join(text) + "\n"
        

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






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

class XKto (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:
            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 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 xxidentify_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 xxidem_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