# coding: windows-1252
#
#---Bezeichnung: E-Rechnung erstellen
#   Klassen: Rechnung
#   CondExpression:
#   ObjectScript: Y
#   ContainerScript: N
#   EventType: Kein
#   EventClass:
#   EventMembers:
#   ExtendedRights: N
#
#---Erstellt eine E-Invoice aufgrund der Spezifikation 3.1 der Swisscom.
#
#---2015-07-31, Vertec AG: Version 100: Erstellt.
#   2016-07-25, Vertec AG: Version 101: Systemproperty umbenannt. Header updated.
#

Version = 101

# Ablauf:
# invoiceIsComputable():
# Prüft generelle Kriterien die nicht mit Attributen im XML zusammenhängen.
# calculateValues():
# Berechnet alle Werte anhand der OCL und speichert das Resultat.
# setAttributesUse():
# Bestimmt welche Elemente im XML wirklich gebraucht werden.
# allRequiredPresent():
# Prüft ob alle benötigten Elemente einen Wert haben.
# generateTree():
# Generiert den XML-Text.
# printXML():
# Erstellt die XML Datei.
# printPDF():
# Erstellt die PDF Datei.


import xml.etree.ElementTree as ElementTree
import collections
import datetime
import os
from win32com.client import Dispatch


class attrObj():
    def __init__(self, required, ocl, eTreeParent, attributes, error):
        self.required = required
        self.ocl = ocl
        self.value = ''
        self.eTreeElement = None
        self.eTreeParent = eTreeParent
        self.attributes = attributes
        self.error = error


class myClass():
    """Meine Klasse"""
    def __init__(self, rechnung):
        self.rechnung = rechnung
        self.missingAttributes = []
        self.errors = []
        self.xml = ''
        # Hier machen wir ein Dictionary, welches sortiert ist und bleibt.
        # Es repräsentiert das XML.
        # Key: Name des Elements im XML, Value: Ein Objekt mit zum Element gehörenden Attributen.
        # Achtung: Die Namen der Elemente im XML sind nicht unique, deshalb wurden den Keys 2-stellige Identifikatoren (2 beliebige Zeichen) angehängt.
        # Diese werden beim Erstellen des XML wieder entfernt.
        # Wichtig ist, dass wenn man ein bestimmtes Element im Dictionary braucht (z.B. in setAttributesUse()),
        # man den richtigen Key mit richtigem Identifikator verwendet.
        self.d = collections.OrderedDict()

        # addAttrObj(name, req.,  ocl, parent, attributes (optional), error (optional))
        self.addAttrObj('Invoice', True,  "", None, {'doctype': "ETS Invoice", 'version': '3.1'}, 'Invoice doctype="ETS Invoice" version="3.1"')
        self.addAttrObj('Invoice_Header', True,  "", 'Invoice')
        # I.H.010_Basisdaten
        self.addAttrObj('I.H.010_Basisdaten', True,  "", 'Invoice_Header')
        self.addAttrObj('BV.010_Rechnungsnummer;01', True,  "nummer", 'I.H.010_Basisdaten')
        self.addAttrObj('BV.020_Rechnungsdatum;01', True,  "datum.asstring", 'I.H.010_Basisdaten')
        self.addAttrObj('BV.025_Buchungsdatum;01', False, "valutadatum.asstring", 'I.H.010_Basisdaten')
        self.addAttrObj('BV.030_Funktion_des_Dokuments;01', True,  "'Original'", 'I.H.010_Basisdaten')
        self.addAttrObj('BV.040_Typ_des_Dokuments;01', True,  "'Rechnung'", 'I.H.010_Basisdaten')
        self.addAttrObj('BV.050_Rechnungs_Endkennzeichen;01', True,  "'vollstaendige Rechnung'", 'I.H.010_Basisdaten')
        self.addAttrObj('BV.080_Waehrung;01', True,  "waehrung.asstring", 'I.H.010_Basisdaten')
        self.addAttrObj('BV.090_Sprache;01',  True,  "projekt.sprache.asstring.tolower", 'I.H.010_Basisdaten')
        self.addAttrObj('BV.095_Abschlussdatum_der_Lieferung_Ausfuehrung;01', False, "bis.asstring", 'I.H.010_Basisdaten')
        self.addAttrObj('BV.120_Liefer_Dienstleistungsperiode;01', False, "if ((von.month = bis.month) and (von.year = bis.year) and (von.day = 1) and (bis.day = bis.lastofmonth.day)) then bis.formatdatetime('mmmm yyyy') else von.asstring + '-' + bis.asstring endif", 'I.H.010_Basisdaten')
        # I.H.020_Einkaeufer_Identifikation
        self.addAttrObj('I.H.020_Einkaeufer_Identifikation', True,  "", 'Invoice_Header')
        self.addAttrObj('BV.010_Nr_Kaeufer_beim_Lieferanten;01', True,  "projekt.kunde.oclastype(Adresseintrag).kundennr", 'I.H.020_Einkaeufer_Identifikation')
        self.addAttrObj('BV.070_Strasse;01', False, "projekt.kunde.oclastype(Adresseintrag).standardAdresse.replaceregex('\r\n',', ')", 'I.H.020_Einkaeufer_Identifikation')
        self.addAttrObj('BV.100_PLZ;01', True,  "projekt.kunde.oclastype(Adresseintrag).standardplz", 'I.H.020_Einkaeufer_Identifikation')
        self.addAttrObj('BV.110_Stadt;01', True,  "projekt.kunde.oclastype(Adresseintrag).standardort", 'I.H.020_Einkaeufer_Identifikation')
        self.addAttrObj('BV.120_Land;01', True,  "projekt.kunde.oclastype(Adresseintrag).standardland", 'I.H.020_Einkaeufer_Identifikation')
        # I.H.030_Lieferanten_Identifikation
        self.addAttrObj('I.H.030_Lieferanten_Identifikation', True,  "", 'Invoice_Header')
        self.addAttrObj('BV.010_Nr_Lieferant_beim_Kaeufer;02', True,  "projekt.kunde.oclastype(Adresseintrag).lieferantennr", 'I.H.030_Lieferanten_Identifikation')
        self.addAttrObj('BV.040_Name1;02', True,  "Objectproperty.allinstances->select(propertyName='Firma')->first.propertyValue.oclAsType(Adresseintrag).name", 'I.H.030_Lieferanten_Identifikation')
        self.addAttrObj('BV.070_Strasse;02', False, "Objectproperty.allinstances->select(propertyName='Firma')->first.propertyValue.oclAsType(Adresseintrag).standardAdresse.replaceregex('\r\n',', ')", 'I.H.030_Lieferanten_Identifikation')
        self.addAttrObj('BV.100_PLZ;02', True,  "Objectproperty.allinstances->select(propertyName='Firma')->first.propertyValue.oclAsType(Adresseintrag).standardplz", 'I.H.030_Lieferanten_Identifikation')
        self.addAttrObj('BV.110_Stadt;02', True, "Objectproperty.allinstances->select(propertyName='Firma')->first.propertyValue.oclAsType(Adresseintrag).standardort", 'I.H.030_Lieferanten_Identifikation')
        self.addAttrObj('BV.120_Land;02', True, "Objectproperty.allinstances->select(propertyName='Firma')->first.propertyValue.oclAsType(Adresseintrag).standardland", 'I.H.030_Lieferanten_Identifikation')
        # I.H.040_Rechnungsadresse
        self.addAttrObj('I.H.040_Rechnungsadresse', True, "", 'Invoice_Header')
        self.addAttrObj('BV.040_Name1;03', True, "rechnungsadresse.oclastype(Adresseintrag).name", 'I.H.040_Rechnungsadresse')
        self.addAttrObj('BV.080_Strasse;03', False, "rechnungsadresse.oclastype(Adresseintrag).standardadresse.replaceregex('\r\n',', ')", 'I.H.040_Rechnungsadresse')
        self.addAttrObj('BV.100_PLZ;03', True, "rechnungsadresse.oclastype(Adresseintrag).standardplz", 'I.H.040_Rechnungsadresse')
        self.addAttrObj('BV.110_Stadt;03', True, "rechnungsadresse.oclastype(Adresseintrag).standardort", 'I.H.040_Rechnungsadresse')
        self.addAttrObj('BV.120_Land;03', True, "rechnungsadresse.oclastype(Adresseintrag).standardland", 'I.H.040_Rechnungsadresse')
        # I.H.080_Zahlungsbedingungen
        self.addAttrObj('I.H.080_Zahlungsbedingungen', True, "", 'Invoice_Header')
        self.addAttrObj('BV.010_Zahlungsbedingungen;12', True, "'Faelligkeitsdatum'", 'I.H.080_Zahlungsbedingungen')
        self.addAttrObj('BV.020_Zahlungsbedingungen_Zusatzwert;12', True, "datum->incDay(debikondition->oclAsType(DebiKondition).frist).asstring", 'I.H.080_Zahlungsbedingungen')
        # I.H.100_Zahlung_mit_Einzahlungsschein
        self.addAttrObj('I.H.100_Zahlung_mit_Einzahlungsschein', True, "", 'Invoice_Header')
        self.addAttrObj('BV.010_ESR_Nummer;04', True, "vesrreferenznr.replacestring(' ','')", 'I.H.100_Zahlung_mit_Einzahlungsschein')
        self.addAttrObj('BV.020_ESR_Teilnehmernummer;04', False, "esrteilnehmer", 'I.H.100_Zahlung_mit_Einzahlungsschein')
        self.addAttrObj('BV.030_ESR_Pruefziffer;04', False, "vESRReferenzNr.substring(vESRReferenzNr.length,vESRReferenzNr.length)", 'I.H.100_Zahlung_mit_Einzahlungsschein')
        self.addAttrObj('BV.040_ESR_Belegart_Code;04', False, "'01'", 'I.H.100_Zahlung_mit_Einzahlungsschein')
		# I.H.120_Zahlung_auf_Bankkonto
        self.addAttrObj('I.H.120_Zahlung_auf_Bankkonto', True, "", 'Invoice_Header')
        self.addAttrObj('BV.010_Name_des_Kontoinhabers;05', False, "", 'I.H.120_Zahlung_auf_Bankkonto')
        self.addAttrObj('BV.020_Kontonummer;05', False, "", 'I.H.120_Zahlung_auf_Bankkonto')
        self.addAttrObj('BV.025_IBAN_Nummer;05', False, "", 'I.H.120_Zahlung_auf_Bankkonto')
        self.addAttrObj('BV.030_Bezeichnung_des_Finanzinstituts;05', True, "", 'I.H.120_Zahlung_auf_Bankkonto')
        self.addAttrObj('BV.040_Branch_Code_der_Bank;05', False, "", 'I.H.120_Zahlung_auf_Bankkonto')
         # I.H.140_MwSt._Informationen
        self.addAttrObj('I.H.140_MwSt._Informationen', True, "", 'Invoice_Header')
        self.addAttrObj('BV.010_Eingetragener_Name_des_Lieferanten;06', True, "Objectproperty.allinstances->select(propertyName='Firma')->first.propertyValue.oclAsType(Adresseintrag).name", 'I.H.140_MwSt._Informationen')
        self.addAttrObj('BV.020_MwSt_Nummer_des_Lieferanten;06', True, "Objectproperty.allinstances->select(propertyName='Firma')->first.propertyValue.oclAsType(Adresseintrag).mwstnr.replaceregex('\W','').substring(1,12)", 'I.H.140_MwSt._Informationen')
        self.addAttrObj('BV.030_Eingetragener_Name_des_Einkaeufers;06', False, "projekt.kunde.oclastype(Adresseintrag).name", 'I.H.140_MwSt._Informationen')
        self.addAttrObj('BV.040_MwSt_Nummer_des_Einkaeufers;06', True, "projekt.kunde.oclastype(Adresseintrag).mwstnr.replaceregex('\W','').substring(1,12)", 'I.H.140_MwSt._Informationen')
        self.addAttrObj('Invoice_Detail', True, "", 'Invoice')
        self.addAttrObj('Invoice_Items', True, "", 'Invoice_Detail')
        # I.D.010_Basisdaten
        self.addAttrObj('I.D.010_Basisdaten', True, "", 'Invoice_Items')
        self.addAttrObj('BV.010_Positions_Nr_in_der_Rechnung;07', True, "'1'", 'I.D.010_Basisdaten')
        self.addAttrObj('BV.070_Artikel_Beschreibung;07', True, "'Dienstleistungen'", 'I.D.010_Basisdaten')
        # I.D.020_Preise_und_Mengen
        self.addAttrObj('I.D.020_Preise_und_Mengen', True, "", 'Invoice_Items')
        self.addAttrObj('BV.010_Verrechnete_Menge;08', True, "'1'", 'I.D.020_Preise_und_Mengen')
        self.addAttrObj('BV.020_Mengeneinheit_der_verrechneten_Menge;08', True, "'EA'", 'I.D.020_Preise_und_Mengen')
        self.addAttrObj('BV.030_Verrechneter_Einzelpreis_des_Artikels;08', True, "(umsatz + vorschussbetrag - vorschusseffektiv)", 'I.D.020_Preise_und_Mengen')
        self.addAttrObj('BV.040_Waehrung_des_Einzelpreises;08', True, "waehrung.asstring", 'I.D.020_Preise_und_Mengen')
        self.addAttrObj('BV.070_Bestaetigter_Gesamtpreis_der_Position_netto;08', True, "(umsatz + vorschussbetrag - vorschusseffektiv)", 'I.D.020_Preise_und_Mengen')
        self.addAttrObj('BV.080_Bestaetigter_Gesamtpreis_der_Position_brutto;08', True, "total", 'I.D.020_Preise_und_Mengen')
        self.addAttrObj('BV.090_Waehrung_des_Gesamtpreises;08', True, "waehrung.asstring", 'I.D.020_Preise_und_Mengen')
        # 'I.D.030_Steuern
        self.addAttrObj('I.D.030_Steuern', True, "", 'Invoice_Items')
        self.addAttrObj('BV.010_Funktion_der_Steuer;09', True, "'Steuer'", 'I.D.030_Steuern')
        self.addAttrObj('BV.030_Steuersatz;09', True, "mwsttyp.oclastype(MwstTyp).satz", 'I.D.030_Steuern')
        self.addAttrObj('BV.040_Zu_versteuernder_Betrag;09', True, "(umsatz + vorschussbetrag - vorschusseffektiv)", 'I.D.030_Steuern')
        self.addAttrObj('BV.045_Waehrung_zu_versteuernder_Betrag;09', False, "waehrung.asstring", 'I.D.030_Steuern')
        self.addAttrObj('BV.050_Steuerbetrag;09', True, "(leistmwst + spesenmwst + auslagenmwst + vorschussmwst)", 'I.D.030_Steuern')
        self.addAttrObj('Invoice_Summary', True, "", 'Invoice')
        # I.S.010_Basisdaten
        self.addAttrObj('I.S.010_Basisdaten', True, "", 'Invoice_Summary')
        self.addAttrObj('BV.010_Anzahl_der_Rechnungspositionen;10', True, "'1'", 'I.S.010_Basisdaten')
        self.addAttrObj('BV.020_Gesamtbetrag_der_Rechnung_exkl_MwSt_exkl_Ab_Zuschlag;10', True, "(umsatz + vorschussbetrag - vorschusseffektiv)", 'I.S.010_Basisdaten')
        self.addAttrObj('BV.030_Waehrung_Gesamtbetrag_der_Rechnung_exkl_MwSt_exkl_Ab_Zuschlag;10', True, "waehrung.asstring", 'I.S.010_Basisdaten')
        self.addAttrObj('BV.040_Gesamtbetrag_der_Rechnung_exkl_MwSt_inkl_Ab_Zuschlag;10', True, "(umsatz + vorschussbetrag - vorschusseffektiv)", 'I.S.010_Basisdaten')
        self.addAttrObj('BV.050_Waehrung_Gesamtbetrag_der_Rechnung_exkl_MwSt_inkl_Ab_Zuschlag;10', True, "waehrung.asstring", 'I.S.010_Basisdaten')
        self.addAttrObj('BV.060_Steuerbetrag;10', True, "(leistmwst + spesenmwst + auslagenmwst + vorschussmwst)", 'I.S.010_Basisdaten')
        self.addAttrObj('BV.070_Waehrung_des_Steuerbetrags;10', True, "waehrung.asstring", 'I.S.010_Basisdaten')
        self.addAttrObj('BV.080_Gesamtbetrag_der_Rechnung_inkl_MwSt_inkl_Ab_Zuschlag;10', True, "total", 'I.S.010_Basisdaten')
        self.addAttrObj('BV.090_Waehrung_Gesamtbetrag_der_Rechnung_inkl_MwSt_inkl_Ab_Zuschlag;10', True, "waehrung.asstring", 'I.S.010_Basisdaten')
        # I.S.020_Aufschluesselung_der_Steuern
        self.addAttrObj('I.S.020_Aufschluesselung_der_Steuern', True, "", 'Invoice_Summary')
        self.addAttrObj('BV.010_Funktion_der_Steuer;11', True, "'Steuer'", 'I.S.020_Aufschluesselung_der_Steuern')
        self.addAttrObj('BV.020_Steuersatz_Kategorie;11', True, "'Standard Satz'", 'I.S.020_Aufschluesselung_der_Steuern')
        self.addAttrObj('BV.030_Steuersatz;11', True, "mwsttyp.oclastype(MwstTyp).satz", 'I.S.020_Aufschluesselung_der_Steuern')
        self.addAttrObj('BV.040_Zu_versteuernder_Betrag;11', False, "(umsatz + vorschussbetrag - vorschusseffektiv)", 'I.S.020_Aufschluesselung_der_Steuern')
        self.addAttrObj('BV.045_Waehrung_zu_versteuernder_Betrag;11', False, "waehrung.asstring", 'I.S.020_Aufschluesselung_der_Steuern')
        self.addAttrObj('BV.050_Steuerbetrag;11', True, "(leistmwst + spesenmwst + auslagenmwst + vorschussmwst)", 'I.S.020_Aufschluesselung_der_Steuern')
        self.addAttrObj('BV.055_Waehrung_Steuerbetrag;11', True, "waehrung.asstring", 'I.S.020_Aufschluesselung_der_Steuern')

    def addAttrObj(self, name, required, ocl, parent, attributes={}, error=None):
        """Wrapper für append-Methode um den (optionalen) Errorstring zu setzen."""
        if error == None:
            error = self.getElementName(name)

        obj = attrObj(required, ocl, parent, attributes, error)
        self.d[name] = obj

    def unifyList(self, seq):
        """Unifiziert eine Liste, eliminiert zusätzlich alle leeren Einträge."""
        seen = set()
        seen_add = seen.add
        return [x for x in seq if x and not (x in seen or seen_add(x))]

    def indent(self, elem, level=0):
        """Handmade prettyprint."""
        i = "\n" + level*"  "
        if len(elem):
            if not elem.text or not elem.text.strip():
                elem.text = i + "  "
            if not elem.tail or not elem.tail.strip():
                elem.tail = i
            for elem in elem:
                self.indent(elem, level+1)
            if not elem.tail or not elem.tail.strip():
                elem.tail = i
        else:
            if level and (not elem.tail or not elem.tail.strip()):
                elem.tail = i

    def invoiceIsComputable(self):
        """Entscheidet ob die Rechnung grundsätzlich verarbeitet werden kann."""
        # Wir brauchen die Exportpfade für die XML- und PDF-Dateien.
        self.xmlPfad = vtcapp.evalocl("stringproperty->select(propertyname='cxtExportXML')->first.propertyvalue")
        self.pdfPfad = vtcapp.evalocl("stringproperty->select(propertyname='cxtExportPDF')->first.propertyvalue")
        if not self.xmlPfad or not self.pdfPfad:
            vtcapp.msgbox("Der Pfad für den Export der E-Rechnungen wurde nicht gefunden.\nÜberprüfen Sie Ihre Systemeinstellungen.")
            return
        # Wir verarbeiten nur verrechnete Rechnungen
        if not self.rechnung.verrechnet:
            vtcapp.msgbox("Es können nur verrechnete Rechnungen als E-Rechnung versandt werden.")
            return
        # Wir verarbeiten nur Rechnungen mit einem Betrag != 0
        if not self.rechnung.total:
            vtcapp.msgbox("Es können nur Rechnungen mit einem Totalbetrag ungleich 0 als E-Rechnungen versandt werden.")
            return
        # Wir verarbeiten nur Rechnungen mit einem bis-Datum, ausser Vorschussrechnungen, die brauchen das nicht.
        if not self.rechnung.eval("vorschuesseAufRechnung"):
            if not self.rechnung.bis:
                vtcapp.msgbox('Es können nur Rechnungen mit einem "Bis" Datum in der Rechnungsperiode als E-Rechnungen versandt werden.')
                return
        # Wir verarbeiten nur Rechnungen mit einem einzigen MWST-Satz drauf.
        mwstCodes = self.unifyList(self.rechnung.eval("buchungsbeleg.oclastype(Beleg).buchungen.mwstcode"))
        if len(mwstCodes) > 1:
            vtcapp.msgbox("Es können nur Rechnungen mit einem einzigen MWSt-Satz als E-Rechnungen versandt werden.")
            return

        # Still here? On with the show!
        return True

    def calculateValues(self):
        """Berechnet die Values mittels OCL, ausgehend von der Rechnung."""
        for fieldname, attrObj in self.d.iteritems():
            if attrObj.ocl:
                expr = attrObj.ocl
                value = self.rechnung.eval(expr)
                if value:
                    # Datum korrekt formatieren
                    try:
                        d = datetime.datetime.strptime(value, '%d.%m.%Y')
                        value = d.strftime('%Y%m%d000000')
                    except:
                        pass
                    # Beträge korrekt formatieren
                    if isinstance(value, float):
                        value = '{0:.2f}'.format(value)

                    attrObj.value = value
                    # Optionale Attribute einschalten
                    attrObj.required = True

    def setFuzzyAttributesRequired(self):
        """Markiert die conditional required Attribute als required Yes/No.
        Die required Attribute komen ins XML File, die anderen nicht."""
        # I.H.120_Zahlung_auf_Bankkonto:
        # IBAN oder oldschool, entweder oder.
        if self.d['BV.025_IBAN_Nummer;05'].value:
            self.d['BV.025_IBAN_Nummer;05'].required = True
        else:
            self.d['BV.020_Kontonummer;05'].required = True
            self.d['BV.040_Branch_Code_der_Bank;05'].required = True
        # I.S.020_Aufschluesselung_der_Steuern:
        # Wenn BV.040, dann auch BV.045, sonst beide weglassen.
        if self.d['BV.040_Zu_versteuernder_Betrag;11'].value:
            self.d['BV.040_Zu_versteuernder_Betrag;11'].reqired = True
            self.d['BV.045_Waehrung_zu_versteuernder_Betrag;11'].required = True
        # I.H.100_Zahlung_mit_Einzahlungsschein vs. I.H.120_Zahlung_auf_Bankkonto
        if self.d['BV.010_ESR_Nummer;04'].value:
            self.d['I.H.120_Zahlung_auf_Bankkonto'].required = False
            self.d['BV.010_Name_des_Kontoinhabers;05'].required = False
            self.d['BV.020_Kontonummer;05'].required = False
            self.d['BV.025_IBAN_Nummer;05'].required = False
            self.d['BV.030_Bezeichnung_des_Finanzinstituts;05'].required = False
            self.d['BV.040_Branch_Code_der_Bank;05'].required = False
        else:
            self.d['I.H.100_Zahlung_mit_Einzahlungsschein'].required = False
            self.d['BV.010_ESR_Nummer;04'].required = False
            self.d['BV.020_ESR_Teilnehmernummer;04'].required = False
            self.d['BV.030_ESR_Pruefziffer;04'].required = False
            self.d['BV.040_ESR_Belegart_Code;04'].required = False

    def allRequiredPresent(self):
        """Prüft ob alle benötigten Felder vonhanden sind."""
        allPresent = True
        for fieldname, attrObj in self.d.iteritems():
            if attrObj.ocl:
                # Die Elemente ohne OCL sind die Nodes (Nicht-Leafs), und müssen nicht geprüft werden, sie werden alle verlangt.
                if attrObj.required and not attrObj.value:
                    self.missingAttributes.append(str(attrObj.error) + ': ' + str(attrObj.ocl))

        if len(self.missingAttributes):
            message = 'Folgende Eigenschaften auf der Rechnung fehlen:\n%s' %'\n'.join(self.missingAttributes)
            print(message)
            vtcapp.msgbox(message)
            allPresent = False
        return allPresent

    def getElementName(self, fieldname):
        """Gibt den Namen eines Elements im XML zurück, ohne den 2stelligen Identifier."""
        if ';' in fieldname:
            return fieldname[:-3]
        return fieldname

    def generateTree(self):
        """Generiert eine elementTree Struktur aus dem Dictionary."""
        for fieldname, attrObj in self.d.iteritems():
            fieldname = self.getElementName(fieldname)
            if not attrObj.eTreeParent:
                # Hat kein Parent, ist das Rootelement.
                attrObj.eTreeElement = ElementTree.Element(fieldname, attrObj.attributes)
            else:
                # Hat Parent, wird Subelement.
                if attrObj.required:
                    if attrObj.value:
                        # Leafs
                        attrObj.eTreeElement = ElementTree.SubElement(self.d[attrObj.eTreeParent].eTreeElement, fieldname, attrObj.attributes).text = attrObj.value
                    else:
                        # Internodes
                        attrObj.eTreeElement = ElementTree.SubElement(self.d[attrObj.eTreeParent].eTreeElement, fieldname, attrObj.attributes)


    def printXML(self):
        """Saves invoice as XML."""
        # Prettify tree: Not all of us are machines who like our xml in one long string.
        self.indent(self.d['Invoice'].eTreeElement)
        # Generate tree
        tree = ElementTree.ElementTree(self.d['Invoice'].eTreeElement)
        # Write pretty tree to file
        fileName = 'Rechnung_%s.xml' %self.rechnung.nummer
        fullPath = os.path.join(self.xmlPfad, fileName)
        try:
            tree.write(fullPath, encoding='utf-8', xml_declaration=True)
        except:
            raise
        # Print tree to stdout for debugging
        #print ElementTree.tostring(self.d['Invoice'].eTreeElement, encoding='utf-8', method='xml')

    def printPDF(self):
        """Generates invoice and saves it as PDF."""
        # Berichtobjekt laden, abbruch wenn nicht gefunden.
        berichtObj = vtcapp.evalocl("bericht->select(berichtname='Rechnung mit Leistungsliste')->first")
        if not berichtObj:
            vtcapp.msgbox("Der Rechnungsbericht wurde nicht gefunden.\nBitte überprüfen Sie Ihre Systemeinstellungen.")
            return

        # Word öffnen
        word = Dispatch('Word.Application')
        word.visible = False #doesn't work

        # Report ausführen, Worddatei speichern.
        worddocPfad = os.path.join(self.pdfPfad, 'Rechnung_%s.docx' %self.rechnung.nummer)
        vtcapp.executereport(self.rechnung, None, berichtObj, '', False, False)

         # Worddokument öffnen
        worddoc = word.ActiveDocument

        # Speichern als PDF
        pdfdocPfad = '%s_ATT_Beilage1.pdf' %worddocPfad[:-5]
        worddoc.SaveAs2(pdfdocPfad, 17)
        worddoc.Close(SaveChanges=False)
        word.Quit()


    def main(self):
        """Hauptmethode"""
        if self.invoiceIsComputable():
            print 'Is computable'
            self.calculateValues()
            print 'Values calculated'
            self.setFuzzyAttributesRequired()
            print 'Fuzzy attributes set'
            if self.allRequiredPresent():
                print 'All required present'
                self.generateTree()
                print 'Tree generated'
                self.printPDF()
                print 'PDF File generated'
                self.printXML()
                print 'XML File generated'
                print 'Done'
                vtcapp.msgbox('Die E-Rechnung wurde erstellt.')
        else:
            print 'Not computable'



processor = myClass(argobject)
processor.main()

