
| 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 |
| 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:])