Verbesserungen GTK App zum prüfen von VAT Nummern

Programmierung für GNOME und GTK+, GUI-Erstellung mit Glade.
Antworten
Benutzeravatar
martinjo
User
Beiträge: 181
Registriert: Dienstag 14. Juni 2011, 20:03

Freitag 14. März 2014, 00:24

Hallo,

ich habe mit heute ein kleines Programm gebastelt um die Umsatzsteueridentifikationsnummern(VAT) ausländischer Unternehmen zu prüfen. Nun wollte ich das Ganze auch anderen zur Verfügung stellen und habe schon einen Account bei GitHub erstellt. Vor dem hochladen würde ich jedoch gerne noch ein paar Dinge verbessern und würde mich über Kommentare zu dem Code freuen.

Man trägt die eigene und die zu prüfende VAT-Nummer ein und mit einem klick auf den "Check"-Button wir eine Anfrage mittels xmlrpc an das Bundeszentralamt für Steuern gesendet. Die Werte des zurückgelieferte Ergebnisses werden mittels xml.etree.ElementTree ausgelesen und als Dict gespeichert. Einer dieser Werte ist der "ErrorCode". Mit diesem wird die dazu passende Erläuterung aus dem "error_dict" geladen. Die Erläuterung sowie alle zurückgelieferten Werte werden in einem TextView angezeigt.

Bild

Hinweis: Mit "Request Confirmation via Letter" kann man eine schriftliche Bestätigung anfragen. Bitte nicht zum Spaß drücken.

Mehr Informationen dazu:
https://evatr.bff-online.de/eVatR/xmlrpc/

Probleme habe ich mit der Strukturierung. Auf Fehler bin ich gar nicht eingegangen und auch die Werte auslesen und anzeigen erinnert eher an einen Hack.

Wie gesagt, freue mich über Rückmeldungen und Verbesserungsvorschläge.

Code: Alles auswählen

#!/usr/bin/env python
#-*- coding: utf-8 -*-

import gtk
import xmlrpclib
import xml.etree.ElementTree as ET

server_url = 'https://evatr.bff-online.de/'
server     = xmlrpclib.Server(server_url)
xmlrpclib.socket.setdefaulttimeout(5)

error_dict = {
"200" : "Die angefragte USt-IdNr. ist 219.",
"201" : "Die angefragte USt-IdNr. ist ungültig.",
"202" : "Die angefragte USt-IdNr. ist ungültig. Sie ist nicht in der Unternehmerdatei des betreffenden EU-Mitgliedstaates registriert.\nHinweis:\nIhr Geschäftspartner kann seine gültige USt-IdNr. bei der für ihn zuständigen Finanzbehörde in Erfahrung bringen. Möglicherweise muss er einen Antrag stellen, damit seine USt-IdNr. in die Datenbank aufgenommen wird.",
"203" : "Die angefragte USt-IdNr. ist ungültig. Sie ist erst ab dem ... gültig (siehe Feld 'Gueltig_ab').",
"204" : "Die angefragte USt-IdNr. ist ungültig. Sie war im Zeitraum von ... bis ... gültig (siehe Feld 'Gueltig_ab' und 'Gueltig_bis').",
"205" : "Ihre Anfrage kann derzeit durch den angefragten EU-Mitgliedstaat oder aus anderen Gründen nicht beantwortet werden. Bitte versuchen Sie es später noch einmal. Bei wiederholten Problemen wenden Sie sich bitte an das Bundeszentralamt für Steuern - Dienstsitz Saarlouis.",
"206" : "Ihre deutsche USt-IdNr. ist ungültig. Eine Bestätigungsanfrage ist daher nicht möglich. Den Grund hierfür können Sie beim Bundeszentralamt für Steuern - Dienstsitz Saarlouis - erfragen.",
"207" : "Ihnen wurde die deutsche USt-IdNr. ausschliesslich zu Zwecken der Besteuerung des innergemeinschaftlichen Erwerbs erteilt. Sie sind somit nicht berechtigt, Bestätigungsanfragen zu stellen.",
"208" : "Für die von Ihnen angefragte USt-IdNr. läuft gerade eine Anfrage von einem anderen Nutzer. Eine Bearbeitung ist daher nicht möglich. Bitte versuchen Sie es später noch einmal.",
"209" : "Die angefragte USt-IdNr. ist ungültig. Sie entspricht nicht dem Aufbau der für diesen EU-Mitgliedstaat gilt. ( Aufbau der USt-IdNr. aller EU-Länder)",
"210" : "Die angefragte USt-IdNr. ist ungültig. Sie entspricht nicht den Prüfziffernregeln die für diesen EU-Mitgliedstaat gelten.",
"211" : "Die angefragte USt-IdNr. ist ungültig. Sie enthält unzulässige Zeichen.",
"212" : "Die angefragte USt-IdNr. ist ungültig. Sie enthält ein unzulässiges Länderkennzeichen.",
"213" : "Die Abfrage einer deutschen USt-IdNr. ist nicht möglich.",
"214" : "Ihre deutsche USt-IdNr. ist fehlerhaft. Sie beginnt mit 'DE' gefolgt von 9 Ziffern.",
"215" : "Ihre Anfrage enthält nicht alle notwendigen Angaben für eine einfache Bestätigungsanfrage (Ihre deutsche USt-IdNr. und die ausl. USt-IdNr.).\nIhre Anfrage kann deshalb nicht bearbeitet werden.",
"216" : "Ihre Anfrage enthält nicht alle notwendigen Angaben für eine qualifizierte Bestätigungsanfrage (Ihre deutsche USt-IdNr., die ausl. USt-IdNr., Firmenname einschl. Rechtsform und Ort).\nEs wurde eine einfache Bestätigungsanfrage durchgeführt mit folgenden Ergebnis:\nDie angefragte USt-IdNr. ist gültig.",
"217" : "Bei der Verarbeitung der Daten aus dem angefragten EU-Mitgliedstaat ist ein Fehler aufgetreten. Ihre Anfrage kann deshalb nicht bearbeitet werden.",
"218" : "Eine qualifizierte Bestätigung ist zur Zeit nicht möglich. Es wurde eine einfache Bestätigungsanfrage mit folgendem Ergebnis durchgeführt:\nDie angefragte USt-IdNr. ist gültig.",
"219" : "Bei der Durchführung der qualifizierten Bestätigungsanfrage ist ein Fehler aufgetreten. Es wurde eine einfache Bestätigungsanfrage mit folgendem Ergebnis durchgeführt:\nDie angefragte USt-IdNr. ist gültig.",
"220" : "Bei der Anforderung der amtlichen Bestätigungsmitteilung ist ein Fehler aufgetreten. Sie werden kein Schreiben erhalten.",
"999" : "Eine Bearbeitung Ihrer Anfrage ist zurzeit nicht möglich. Bitte versuchen Sie es später noch einmal."
}

yellow = "#FFFF66"
green = "#66FF66"
red = "#ff8080"
orange = "#FFAD66"

# Basisklasse
class CheckVAT:
    # define close
    def destroy(selb, widget, data=None):
        gtk.main_quit()
        
    def check(self, widget):

        # necessary
        UstId_1 = self.self_vat_entry.get_text()
        UstId_2 = self.external_vat_entry.get_text()
        
        # extended
        Firmenname = self.external_name_entry.get_text()
        Ort = self.external_city_entry.get_text()
        
        # optional
        PLZ = self.external_zip_code_entry.get_text()
        Strasse = self.external_street_entry.get_text()
        if self.checkbutton_print.get_active():
            Druck = "ja"
        else:
            Druck = "nein"
        
        # request
        rpc = server.evatrRPC(UstId_1, UstId_2, Firmenname, Ort, PLZ, Strasse, Druck)

        # result
        root = ET.fromstring(rpc)
        
        # results to dict
        return_dict={}
        for s_element in root.findall("param"):
            keyvalue = []
            for child in s_element.iter('string'):
                keyvalue.append( child.text )
            return_dict[keyvalue[0]] = keyvalue[1]
            
        
        
        # create result output text
        printdata = ""
        for d in return_dict.keys():
            if return_dict[d]:
                printdata += d+": "+return_dict[d]+"\n"
                
        errorcode = error_dict[return_dict["ErrorCode"]]
        printresults = printdata + "\n\n" + errorcode


        # color output view background
        if return_dict["ErrorCode"] == "200":
            self.content_textview.modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse(green))
        elif return_dict["ErrorCode"] in ["216,219"]:
            self.content_textview.modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse(orange))
        else:
            self.content_textview.modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse(red))

        #show result
        self.content_textbuffer.set_text(printresults)



    def __init__(self):
        # define main window
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.set_position(gtk.WIN_POS_CENTER)
        self.window.set_size_request(600, 500)
        self.window.set_title("Check VAT CODEs")
        
        
        # HBOX 1 - Own Data
        self.hbox1_selfinfo = gtk.VBox()
        
        self.selfinfo_label = gtk.Label("Own Data:")
        self.hbox1_selfinfo.pack_start(self.selfinfo_label)
        
        self.self_vat_box = gtk.HBox(homogeneous=True)
        self.self_vat = ''
        self.self_vat_label = gtk.Label("Own VAT Code (necessary)")
        self.self_vat_entry = gtk.Entry()
        self.self_vat_entry.set_text(self.self_vat)
        self.self_vat_box.pack_start(self.self_vat_label)
        self.self_vat_box.pack_start(self.self_vat_entry)
        
        self.hbox1_selfinfo.pack_start(self.self_vat_box)

        
        # HBox 2 - Firmendaten Externe Firma
        self.hbox2_external = gtk.VBox()
        
        self.hbox2_external_label = gtk.Label("Other Company Data:")
        self.hbox2_external.pack_start(self.hbox2_external_label, expand = False, fill = False)

        # VAT
        self.external_vat_box = gtk.HBox(homogeneous=True)
        self.external_vat = ''
        self.external_vat_label = gtk.Label("VAT Code (necessary)")
        self.external_vat_entry = gtk.Entry()
        self.external_vat_entry.set_text(self.external_vat)
        self.external_vat_box.pack_start(self.external_vat_label)
        self.external_vat_box.pack_start(self.external_vat_entry)
        self.hbox2_external.pack_start(self.external_vat_box, expand = False, fill = False)
        
        # Name
        self.external_name_box = gtk.HBox(homogeneous=True)
        self.external_name = ''
        self.external_name_label = gtk.Label("Name (extended)")
        self.external_name_entry = gtk.Entry()
        self.external_name_entry.set_text(self.external_name)
        self.external_name_box.pack_start(self.external_name_label)
        self.external_name_box.pack_start(self.external_name_entry)
        self.hbox2_external.pack_start(self.external_name_box, expand = False, fill = False)

        # City
        self.external_city_box = gtk.HBox(homogeneous=True)
        self.external_city = ''
        self.external_city_label = gtk.Label("City (extended)")
        self.external_city_entry = gtk.Entry()
        self.external_city_entry.set_text(self.external_city)
        self.external_city_box.pack_start(self.external_city_label)
        self.external_city_box.pack_start(self.external_city_entry) 
        self.hbox2_external.pack_start(self.external_city_box, expand = False, fill = False)
        
        # Zip code
        self.external_zip_code_box = gtk.HBox(homogeneous=True)
        self.external_zip_code = ''
        self.external_zip_code_label = gtk.Label("Zip code (optional)")
        self.external_zip_code_entry = gtk.Entry()
        self.external_zip_code_entry.set_text(self.external_zip_code)
        self.external_zip_code_box.pack_start(self.external_zip_code_label)
        self.external_zip_code_box.pack_start(self.external_zip_code_entry) 
        self.hbox2_external.pack_start(self.external_zip_code_box, expand = False, fill = False)
        
        # Street
        self.external_street_box = gtk.HBox(homogeneous=True)
        self.external_street = ''
        self.external_street_label = gtk.Label("Street (optional)")
        self.external_street_entry = gtk.Entry()
        self.external_street_entry.set_text(self.external_street)
        self.external_street_box.pack_start(self.external_street_label)
        self.external_street_box.pack_start(self.external_street_entry)    
        self.hbox2_external.pack_start(self.external_street_box, expand = False, fill = False)
       
       
        
        
        ## BUTTONS
        self.buttonbox = gtk.HBox()
    
            # Check Button
        self.check_button = gtk.Button("Check")
        self.check_button.connect("clicked", self.check)
        self.buttonbox.pack_start(self.check_button)
        
            # Print Checkbutton
        self.checkbutton_print = gtk.CheckButton("Request Confirmation via Letter")
        self.buttonbox.pack_start(self.checkbutton_print)

            # Exit Button
        self.exitbutton = gtk.Button("Exit")
        self.exitbutton.connect("clicked", self.destroy)
        self.buttonbox.pack_start(self.exitbutton)
        



        ## RESULTS
        self.result_box = gtk.VBox()   
        
            # Label
        self.result_label = gtk.Label("Results:")
        self.result_box.pack_start(self.result_label, expand = False, fill = False)
        
            # Content
        self.content_textbuffer = gtk.TextBuffer()
        self.content_textbuffer.set_text("...")
        self.content_textview = gtk.TextView(buffer=self.content_textbuffer)
        #self.content_textview.modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse(yellow))
        self.content_textview.set_wrap_mode("GTK_WRAP_WORD")
        self.result_box.pack_start(self.content_textview)


        ## Set boxes and content
        self.box1 = gtk.VBox(spacing=8)
        self.box1.pack_start(self.hbox1_selfinfo, expand = False, fill = False)
        self.box1.pack_start(self.hbox2_external, expand = False, fill = False)
        self.box1.pack_start(self.buttonbox, expand = False, fill = False)
        self.box1.pack_start(self.result_box)


        # Add box to main window
        self.window.add(self.box1)
        
        # call all
        self.window.show_all()
        
        #connect exit button and exit function
        self.window.connect("destroy", self.destroy)

    def main(self):
        gtk.main()

if __name__ == "__main__":
    checkvat = CheckVAT()
    checkvat.main()
EyDu
User
Beiträge: 4871
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Freitag 14. März 2014, 00:57

Hallo,

ich gehe das mal von oben nach unten durch:

- Konstanten sollten nach PEP 8 vollständig in Großbuchstaben geschrieben werden. Also SERVER_URL, ERROR_DICT, YELLOW, ...
- Die Fehlercodes hätte ich als Integer implementiert.
- Dem ``error_dict`` würde etwas Einrückung nicht schaden.
- Verwende keine Datentypen in Variablennamen. Die sind unnötig und oft falsch, wenn sich ein Typ doch mal ändert. Oder es müssen alle Vorkommen des Namens angepasst werden, was sehr anstrengend werden kann.
- Falls du nicht mit Python 3.x arbeitest, dann sollte CheckVAT von object erben.
- Variablen sollten in Python klein_und_mit_unterstrichen geschrieben werden. Ist der erste Buchstabe groß, denn ist das ein Indikator für einen Typ.
- Zeilen 61 bis 64 kannst du zusammenfassen:

Code: Alles auswählen

 druck = "ja" if self.checkbutton_print.get_active() else "nein"
- "return_dict" ist wieder so ein schlechter Name
- Verwende konstanten für Strings, also für "param", "string", "ErrorCode" etc.
- Warum iterierst du in Zeile 76 über alle Elemente, wen du in 78 doch nur das erste und das zweite brauchst?
- "keyvalue" ist auch wieder ein schlechter Name. Was stecktda drin?
- Strings sollten nicht mittels += zusammengesetzt werden, dafür gibt es auf Strings eine join-Methode.
- Stringformatierung mittels + ist auch unschön, verwende dazu beser die format-Methode von Strings:

Code: Alles auswählen

print_data = "\n".join("{}:{}" for d in return_dict.keys() if return_dict[d])
- "printdata" und "printresults" sagen wieder nichts aus. Das "print" verwirrt sogar noch, es deutet auf einen Funktion hin
- Zeile 95 macht nicht das, was du erwartest. Ein Fehlercode von "21", "16" oder "19" würde ebenfalls die Bedingung erfüllen. Du meinst ``in ["216", "219"]``
- Zeilen 93 bis 98 kannst du zusammenfassen:

Code: Alles auswählen

if return_dict["ErrorCode"] == "200":
    color = GREEN
elif return_dict["ErrorCode"] in ["216,219"]:
    color = ORANGE
else:
    color = RED

self.content_textview.modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse(color))
- Das erstellen der Labels solltest du in eine Funktion auslagern, du machst da fast identische Dinge.
- Selbiges gilt für die CheckButtons.
- Die ganzen Labels sollten nicht an self gebunden werden, du verwendest sie eh nie wieder. Da reicht ein lokaler Name.
- Die __init__-Methode ist viel zu lang. Mache Methoden nicht länger als eine Bildschirmseite.
- Kommentare sollten einen Mehrwert bieten
- Trenne GUI und Logik
Das Leben ist wie ein Tennisball.
Benutzeravatar
martinjo
User
Beiträge: 181
Registriert: Dienstag 14. Juni 2011, 20:03

Freitag 14. März 2014, 10:44

Da habe ich ja erstmal einiges zu tun :-) Leider werde ich erst nächste Woche zum verbessern kommen aber Danke schon mal für die vielen Tipps.
Benutzeravatar
martinjo
User
Beiträge: 181
Registriert: Dienstag 14. Juni 2011, 20:03

Samstag 16. Mai 2015, 10:49

So, hat nun doch etwas länger gedauert. Nimmt aber auch viel Zeit in Anspruch, war jetzt ein paar Stunden dran und habe mal ein wenig korrigiert.

- Konstanten sollten nach PEP 8 vollständig in Großbuchstaben geschrieben werden. Also SERVER_URL, ERROR_DICT, YELLOW, ...
Habe ich bei den oben genannten verbessert, jedoch nicht bei allem, was man wohl als Konstanten bezeichnen kann. Z.b. im obigen Code bei 120. self.self_vat = '' nicht. Dort schien es mir irgendwie nicht angebracht?
- Die Fehlercodes hätte ich als Integer implementiert.
Habe ich umgesetzt, etwas schade ist, dass daduch der folgende Code nicht mehr funktioniert und durch einen Längeren ersetzt werden muss:

Code: Alles auswählen

elif return_dict["ErrorCode"] in ["216","219"]:
neu:

Code: Alles auswählen

elif error_code == [216] or error_code == [219]:
Bei zwei Werten ist dass noch recht einfach, aber bei mehreren Werten finde ich das "in" angenehmer. Den "error_code" vor dem prüfen mit "in" wieder in einen String umzuwandeln ist auch keine schöne Lösung, oder?
- Dem ``error_dict`` würde etwas Einrückung nicht schaden.
erledigt
- Verwende keine Datentypen in Variablennamen. Die sind unnötig und oft falsch, wenn sich ein Typ doch mal ändert. Oder es müssen alle Vorkommen des Namens angepasst werden, was sehr anstrengend werden kann.
habe ich verbessert
- Falls du nicht mit Python 3.x arbeitest, dann sollte CheckVAT von object erben.
erledigt, muss das zwingend bei Python 3 weggelassen werden? Ich frage da ich mir doch überlege bald mit Python 3 weiter zu machen.
- Variablen sollten in Python klein_und_mit_unterstrichen geschrieben werden. Ist der erste Buchstabe groß, denn ist das ein Indikator für einen Typ.
- Zeilen 61 bis 64 kannst du zusammenfassen...
geändert, Danke

- "return_dict" ist wieder so ein schlechter Name
bei den anderen habe ich es ergänzt, hier möchte ich den Namen jedoch gerne behalten. Ein Dict wird ja nicht so schnell durch eine Liste oder ähnliches ergänzt ohne dass weitere Anpassungen im Code nötig sind. Habe mich irgendwie daran gewöhnt. :?
- Verwende konstanten für Strings, also für "param", "string", "ErrorCode" etc.
erledigt
- Warum iterierst du in Zeile 76 über alle Elemente, wen du in 78 doch nur das erste und das zweite brauchst?
Das war das erste, dass funktioniert hat :oops: . Direkt zugreifen ging nicht:
TypeError: 'generator' object has no attribute '__getitem__'
Es sind aber auch nur zwei Elemente. Probiere jedoch gerne was anderes aus.
- "keyvalue" ist auch wieder ein schlechter Name. Was steckt da drin?
Ein Schlüssel-Werte-Paar, dachte das ist ein recht verständlicher Name, da darin beim ersten Durchlauf ein Schlüssel und beim zweite Durchlauf der dazugehörige Wert gespeichert wird. Den weise ich gleich darauf dem Dict zu.
- Strings sollten nicht mittels += zusammengesetzt werden, dafür gibt es auf Strings eine join-Methode.
korrigiert. Das "+=" ist in Ordnung oder? "print_data += "".join([d, ": ", return_dict[d], "\n"])"
- Stringformatierung mittels + ist auch unschön, verwende dazu beser die format-Methode von Strings:

Code: Alles auswählen

print_data = "\n".join("{}:{}" for d in return_dict.keys() if return_dict[d])
Das habe ich leider so nicht hin bekommen, habe mit format noch nicht gearbeitet und das Beispiel liefert nur leere Klammern als Ergebnis. Habe es auf verschiedene Arten probiert z.b.

Code: Alles auswählen

print_data = "\n".join("{}:{}".format( [ d,return_dict[d] for d in return_dict.keys() if return_dict[d] ] )
- "printdata" und "printresults" sagen wieder nichts aus. Das "print" verwirrt sogar noch, es deutet auf einen Funktion hin
Habe ich duch "view_data" und "result_view_text" auch wenn das wohl auch nicht viel besser ist, aber kein print mehr :-). Was besseres ist mir nicht eingefallen.
- Zeile 95 macht nicht das, was du erwartest. Ein Fehlercode von "21", "16" oder "19" würde ebenfalls die Bedingung erfüllen. Du meinst ``in ["216", "219"]``
Danke, dass war ein Schreibfahler.
- Zeilen 93 bis 98 kannst du zusammenfassen: ...
Danke, sieht übersichtlicher aus.
- Das erstellen der Labels solltest du in eine Funktion auslagern, du machst da fast identische Dinge.
Nur das Label oder auch "box" und "entry"? Auf das "entry" muss ich später zugreifen. Das Label ist doch nur eine Zeile?
- Selbiges gilt für die CheckButtons.
Habe ich leider nicht hin bekommen.
- Die ganzen Labels sollten nicht an self gebunden werden, du verwendest sie eh nie wieder. Da reicht ein lokaler Name.
korrigiert
- Die __init__-Methode ist viel zu lang. Mache Methoden nicht länger als eine Bildschirmseite.
Ja, dort wird ja fast alles erstellt. Wie gehe ich das am besten an? Den ganzen Aufbau in eine Methode auslagern?
- Kommentare sollten einen Mehrwert bieten
habe ich etwas verbessert
- Trenne GUI und Logik
Das wir schwierig. Da habe ich noch Probleme damit, aber werde versuchen das beim nächsten Durchlauf an zu gehen.



Hier der verbesserte Code:

Code: Alles auswählen

#!/usr/bin/env python
#-*- coding: utf-8 -*-

import gtk
import xmlrpclib

SERVER_URL = 'https://evatr.bff-online.de/'
server     = xmlrpclib.Server(SERVER_URL)
xmlrpclib.socket.setdefaulttimeout(5)


"""
todo

Beim erneuten prüfen sollte die Result Ausgabe zuerst gelöscht werden damit man sieht, dass ausgabe neu
"""

ERROR_CODES = {
                200 : "Die angefragte USt-IdNr. ist gültig.",
                201 : "Die angefragte USt-IdNr. ist ungültig.",
                202 : "Die angefragte USt-IdNr. ist ungültig. Sie ist nicht in der Unternehmerdatei des betreffenden EU-Mitgliedstaates registriert.\nHinweis:\nIhr Geschäftspartner kann seine gültige USt-IdNr. bei der für ihn zuständigen Finanzbehörde in Erfahrung bringen. Möglicherweise muss er einen Antrag stellen, damit seine USt-IdNr. in die Datenbank aufgenommen wird.",
                203 : "Die angefragte USt-IdNr. ist ungültig. Sie ist erst ab dem ... gültig (siehe Feld 'Gueltig_ab').",
                204 : "Die angefragte USt-IdNr. ist ungültig. Sie war im Zeitraum von ... bis ... gültig (siehe Feld 'Gueltig_ab' und 'Gueltig_bis').",
                205 : "Ihre Anfrage kann derzeit durch den angefragten EU-Mitgliedstaat oder aus anderen Gründen nicht beantwortet werden. Bitte versuchen Sie es später noch einmal. Bei wiederholten Problemen wenden Sie sich bitte an das Bundeszentralamt für Steuern - Dienstsitz Saarlouis.",
                206 : "Ihre deutsche USt-IdNr. ist ungültig. Eine Bestätigungsanfrage ist daher nicht möglich. Den Grund hierfür können Sie beim Bundeszentralamt für Steuern - Dienstsitz Saarlouis - erfragen.",
                207 : "Ihnen wurde die deutsche USt-IdNr. ausschliesslich zu Zwecken der Besteuerung des innergemeinschaftlichen Erwerbs erteilt. Sie sind somit nicht berechtigt, Bestätigungsanfragen zu stellen.",
                208 : "Für die von Ihnen angefragte USt-IdNr. läuft gerade eine Anfrage von einem anderen Nutzer. Eine Bearbeitung ist daher nicht möglich. Bitte versuchen Sie es später noch einmal.",
                209 : "Die angefragte USt-IdNr. ist ungültig. Sie entspricht nicht dem Aufbau der für diesen EU-Mitgliedstaat gilt. ( Aufbau der USt-IdNr. aller EU-Länder)",
                210 : "Die angefragte USt-IdNr. ist ungültig. Sie entspricht nicht den Prüfziffernregeln die für diesen EU-Mitgliedstaat gelten.",
                211 : "Die angefragte USt-IdNr. ist ungültig. Sie enthält unzulässige Zeichen.",
                212 : "Die angefragte USt-IdNr. ist ungültig. Sie enthält ein unzulässiges Länderkennzeichen.",
                213 : "Die Abfrage einer deutschen USt-IdNr. ist nicht möglich.",
                214 : "Ihre deutsche USt-IdNr. ist fehlerhaft. Sie beginnt mit 'DE' gefolgt von 9 Ziffern.",
                215 : "Ihre Anfrage enthält nicht alle notwendigen Angaben für eine einfache Bestätigungsanfrage (Ihre deutsche USt-IdNr. und die ausl. USt-IdNr.).\nIhre Anfrage kann deshalb nicht bearbeitet werden.",
                216 : "Ihre Anfrage enthält nicht alle notwendigen Angaben für eine qualifizierte Bestätigungsanfrage (Ihre deutsche USt-IdNr., die ausl. USt-IdNr., Firmenname einschl. Rechtsform und Ort).\nEs wurde eine einfache Bestätigungsanfrage durchgeführt mit folgenden Ergebnis:\nDie angefragte USt-IdNr. ist gültig.",
                217 : "Bei der Verarbeitung der Daten aus dem angefragten EU-Mitgliedstaat ist ein Fehler aufgetreten. Ihre Anfrage kann deshalb nicht bearbeitet werden.",
                218 : "Eine qualifizierte Bestätigung ist zur Zeit nicht möglich. Es wurde eine einfache Bestätigungsanfrage mit folgendem Ergebnis durchgeführt:\nDie angefragte USt-IdNr. ist gültig.",
                219 : "Bei der Durchführung der qualifizierten Bestätigungsanfrage ist ein Fehler aufgetreten. Es wurde eine einfache Bestätigungsanfrage mit folgendem Ergebnis durchgeführt:\nDie angefragte USt-IdNr. ist gültig.",
                220 : "Bei der Anforderung der amtlichen Bestätigungsmitteilung ist ein Fehler aufgetreten. Sie werden kein Schreiben erhalten.",
                999 : "Eine Bearbeitung Ihrer Anfrage ist zurzeit nicht möglich. Bitte versuchen Sie es später noch einmal."
                }

YELLOW = "#FFFF66"
GREEN = "#66FF66"
RED = "#ff8080"
ORANGE = "#FFAD66"

# Basisklasse
class CheckVAT(object):
    
    # define close
    def destroy(selb, widget, data=None):
        gtk.main_quit()
    
    def check(self, widget):

        # necessary values
        UstId_1 = self.self_vat_entry.get_text()
        UstId_2 = self.external_vat_entry.get_text()
        
        # values for extended confirmation
        firmenname = self.external_name_entry.get_text()
        ort = self.external_city_entry.get_text()
        
        # optional values for complete confirmation
        plz = self.external_zip_code_entry.get_text()
        strasse = self.external_street_entry.get_text()
        drucken = "ja" if self.checkbutton_print.get_active() else "nein"
        
        # build RPC request
        rpc = server.evatrRPC(UstId_1, UstId_2, firmenname, ort, plz, strasse, drucken)

        # get result
        import xml.etree.ElementTree as ET
        root = ET.fromstring(rpc)
        
        # save results to dict
        return_dict={}
        PARAM = "param"
        STRING = "string"
        ERRORCODE = "ErrorCode"
        for s_element in root.findall(PARAM):
            keyvalue = []
            for child in s_element.iter(STRING):
                keyvalue.append( child.text )
            return_dict[ keyvalue[0] ] = keyvalue[1]
            
        error_code = int( return_dict[ ERRORCODE ] )
        
        # create result output text
        view_data = ""
        for d in return_dict.keys():
            if return_dict[d]:
                view_data += "".join( [d, ": ", return_dict[d], "\n"] )
                
        response_type = ERROR_CODES[ error_code ]
        result_view_text = "".join( [view_data, "\n\n", response_type] )

        # color output view background
        if error_code == 200:
            color = GREEN
        elif error_code == [216] or error_code == [219]:
            color = ORANGE
        else:
            color = RED
        self.content_textview.modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse(color))
        
        # show result
        self.content_textbuffer.set_text(result_view_text)



    def __init__(self):
        # define main window
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.set_position(gtk.WIN_POS_CENTER)
        self.window.set_size_request(600, 500)
        self.window.set_title("Check VAT CODEs")
        
        # HBox 1 - own data
        hbox1_selfinfo = gtk.VBox()
        selfinfo_label = gtk.Label("Own Data:")
        hbox1_selfinfo.pack_start(selfinfo_label)
        self_vat_box = gtk.HBox(homogeneous=True)
        self_vat = ''
        self_vat_label = gtk.Label("Own VAT Code (necessary)")
        self.self_vat_entry = gtk.Entry()
        self.self_vat_entry.set_text(self_vat)
        self_vat_box.pack_start(self_vat_label)
        self_vat_box.pack_start(self.self_vat_entry)
        hbox1_selfinfo.pack_start(self_vat_box)

        # HBox 2 - external company
        hbox2_external = gtk.VBox()
        hbox2_external_label = gtk.Label("Other Company Data:")
        hbox2_external.pack_start(hbox2_external_label, expand = False, fill = False)

        # vat
        external_vat_box = gtk.HBox(homogeneous=True)
        external_vat = ''
        external_vat_label = gtk.Label("VAT Code (necessary)")
        self.external_vat_entry = gtk.Entry()
        self.external_vat_entry.set_text(external_vat)
        external_vat_box.pack_start(external_vat_label)
        external_vat_box.pack_start(self.external_vat_entry)
        hbox2_external.pack_start(external_vat_box, expand = False, fill = False)
        
        # name
        external_name_box = gtk.HBox(homogeneous=True)
        external_name = ''
        external_name_label = gtk.Label("Name (extended)")
        self.external_name_entry = gtk.Entry()
        self.external_name_entry.set_text(external_name)
        external_name_box.pack_start(external_name_label)
        external_name_box.pack_start(self.external_name_entry)
        hbox2_external.pack_start(external_name_box, expand = False, fill = False)

        # city
        external_city_box = gtk.HBox(homogeneous=True)
        external_city = ''
        external_city_label = gtk.Label("City (extended)")
        self.external_city_entry = gtk.Entry()
        self.external_city_entry.set_text(external_city)
        external_city_box.pack_start(external_city_label)
        external_city_box.pack_start(self.external_city_entry) 
        hbox2_external.pack_start(external_city_box, expand = False, fill = False)
        
        # zip code
        external_zip_code_box = gtk.HBox(homogeneous=True)
        external_zip_code = ''
        external_zip_code_label = gtk.Label("Zip code (optional)")
        self.external_zip_code_entry = gtk.Entry()
        self.external_zip_code_entry.set_text(external_zip_code)
        external_zip_code_box.pack_start(external_zip_code_label)
        external_zip_code_box.pack_start(self.external_zip_code_entry) 
        hbox2_external.pack_start(external_zip_code_box, expand = False, fill = False)
        
        # street
        external_street_box = gtk.HBox(homogeneous=True)
        external_street = ''
        external_street_label = gtk.Label("Street (optional)")
        self.external_street_entry = gtk.Entry()
        self.external_street_entry.set_text(external_street)
        external_street_box.pack_start(external_street_label)
        external_street_box.pack_start(self.external_street_entry)    
        hbox2_external.pack_start(external_street_box, expand = False, fill = False)

        
        # create buttons
        buttonbox = gtk.HBox()
        check_button = gtk.Button("Check")
        check_button.connect("clicked", self.check)
        buttonbox.pack_start(check_button)
        self.checkbutton_print = gtk.CheckButton("Request Confirmation via Letter")
        buttonbox.pack_start(self.checkbutton_print)
        exitbutton = gtk.Button("Exit")
        exitbutton.connect("clicked", self.destroy)
        buttonbox.pack_start(exitbutton)
        
        # create result box
        result_box = gtk.VBox()   
        result_label = gtk.Label("Results:")
        result_box.pack_start(result_label, expand = False, fill = False)
        
        self.content_textbuffer = gtk.TextBuffer()
        self.content_textbuffer.set_text("...")
        self.content_textview = gtk.TextView(buffer=self.content_textbuffer)
        self.content_textview.modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse(YELLOW))
        self.content_textview.set_wrap_mode("GTK_WRAP_WORD")
        result_box.pack_start(self.content_textview)
        
        # create info box
        result_info = gtk.VBox()   
        info_label = gtk.Label(""" A:\tstimmt überein \t\t B:\tstimmt nicht überein \n C:\tnicht angefragt \t\t D:\tvom EU-Mitgliedsstaat nicht mitgeteilt""")
        result_info.pack_start(info_label)
        
        # set up boxes and content
        box1 = gtk.VBox(spacing=8)
        box1.pack_start(hbox1_selfinfo, expand = False, fill = False)
        box1.pack_start(hbox2_external, expand = False, fill = False)
        box1.pack_start(buttonbox, expand = False, fill = False)
        box1.pack_start(result_box)
        box1.pack_start(result_info, expand = False, fill = False)

        # add box to main window
        self.window.add(box1)
        
        # call all
        self.window.show_all()
        
        # connect exit button and exit function
        self.window.connect("destroy", self.destroy)

    def main(self):
        gtk.main()

if __name__ == "__main__":
    checkvat = CheckVAT()
    checkvat.main()
mutetella
User
Beiträge: 1690
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Samstag 16. Mai 2015, 11:03

@martinjo
Mit kleinem Aufwand könntest Du etwas mehr Komfort einbauen: In Deinen `ERROR_CODES` kommen ein paar Einträge wie

Code: Alles auswählen

203 : "Die angefragte USt-IdNr. ist ungültig. Sie ist erst ab dem ... gültig (siehe Feld 'Gueltig_ab')."
vor. Ich als Nutzer frage mich bei solchen Meldungen ('... (siehe Feld 'Gueltig_ab')...') immer, weshalb man das nicht gleich hinschreiben kann, wenn die Daten doch augenscheinlich vorhanden sind. Wenn Du also

Code: Alles auswählen

203 : "Die angefragte USt-IdNr. ist ungültig. Sie ist erst ab dem {:%d.%m.%Y} gültig."
verwendest, könntest Du beim Anzeigen der Meldung die Informationen gleich einfügen:

Code: Alles auswählen

ERROR_CODES[203].format(valid_date)
mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Benutzeravatar
martinjo
User
Beiträge: 181
Registriert: Dienstag 14. Juni 2011, 20:03

Samstag 16. Mai 2015, 11:51

@mutetella Das wird mir etwas zu viel. Dann muss ich wieder testen welcher ErrorCode geliefert und für [203] und [204] anpassen. Zusätzlich kann ich nicht mehr wie jetzt nur alle Rückgabewerte die nicht None sind auflisten. Statt dessen müsste ich gezielt auf die Felder zugreifen, die Werte und deren Vorhandensein prüfen und mit dem Modul datetime umwandeln. Dafür das diese Situation kaum vorkommt und die Werte auch direkt da stehen lohnt es sich meiner Meinung nach nicht. Trotzdem vielen Dank für deine Tipp! Grundsätzlich sehe ich das genau so.
BlackJack

Samstag 16. Mai 2015, 13:01

@martinjo: `self_vat` ist zwar eine Konstante, daber die finde ich an sich unsinnig. Das ist ein Name für eine leere Zeichenkette, lokal in einer Methode definiert und nur *einmal* benutzt. Warum bindet man dafür einen Namen statt einfach an der entsprechenden Stelle eine leere Zeichenkette hinzuschreiben? Oder den Aufruf dort ganz weg zu lassen, denn ein `Entry` startet leer, das noch einmal mit einer leeren Zeichenkette zu ”füllen” ist überflüssig. Das spart dann gleich mal zwei Zeilen pro Eingabefeld. :-)

Wenn aus ``return_dict["ErrorCode"] in ["216","219"]`` durch ändern von Zeichenketten für Fehlercodes zu Zahlen ``error_code == [216] or error_code == [219]`` werden muss dann hast Du beim Umwandeln etwas falsch gemacht, nämlich statt die Zeichenkette in eine Zahl zu wandeln die Zeichenkette in eine Zahl zu wandeln die als einziges Element in einer Liste steht. Warum? Zumal man auch dann immer noch ``error_code in [[216], [219]]`` hätte schreiben können.

Die Einrückung beim `error_dict` ist jetzt etwas sehr hoch. Auch dort würde ich bei den normalen vier Leerzeichen pro Ebene bleiben. Man könnte sich hier auch noch überlegen an der Zeilenlänge zu arbeiten und wie schon mal angesprochen wurde Platzhalter für Werte aus der GUI einzufügen.

Man kann auch in Python 3 noch explizit von `object` erben. Das müsste man also beim Portieren nicht ändern.

Ein `dict` kann zwar nicht so einfach durch eine Liste ersetzt werden, aber durch andere Typen die Schlüssel auf Werte abbilden.

Auf Modulebene sollten nur Konstanten, Funktionen, und Klassen definiert werden. `server` ist weder von der Namensschreibweise noch vom Wert her eine Konstante. Auf den Wert sollte man deshalb auch nicht einfach so aus Methoden heraus zugreifen ohne das er als Argument übergeben wurde, entweder direkt oder als Attribut des Objekts.

Mehrzeilige zeichenketten sind keine Kommentare.

Der Kommentar ``# Basisklasse`` ist falsch. Basisklassen sind solche die als Basis zum Ableiten von neuen Klassen dienen, beziehungsweise bei einer gegebenen Klasse ist die Basisklasse die von der diese Klasse abgeleitet wurde.

Die `__init__` ist normalerweise die erste Methode die in einer Klassendefinition steht, denn diese Methode muss man lesen um zu wissen welche Attribute die Objekte haben, und dann ist das doof wenn man die Methode erst irgendwo innerhalb des Klassenkörpers suchen muss.

Das mit dem `WIN_POS_CENTER` würde ich mir gut überlegen. Wenn das jedes Programm machen würde hätte der Benutzer lauter Fenster in der Mitte des Bildschirms die übereinanderliegen und er müsste die jedes mal selber so verschieben das er alles sehen kann. Eine (möglichst) freie Stelle für ein neues Fenster zu finden ist aber schon die Aufgabe der Fensterverwaltung des Systems auf dem das Programm gestartet wird.

Das Durchnummerieren von Namen ist unschön. Man kann doch statt 1 und 2 auch ganz leicht bessere Namen für die beiden „VAT codes“ wählen.

In der `check()`-Methode werden auch die ganzen Argumente für den RPC-Aufruf an Namen gebunden obwohl die nur für den Aufruf verwendet werden.

Importe gehören an den Anfang vom Modul.

Ein Schlüssel/Wert-Paar wird in der Python-API nicht als `keyvalue` sondern als `item` bezeichnet. Allerdings kann man wenn man da grundsätzlich zwei Werte als Ergebnis erwartet auch einfach ``key, value = param_node.iter(STRING)`` schreiben um diese beiden Werte den zwei Namen zuzuordnen. Dann kann man das ganze aber noch weiter vereinfachen und einen Generatorausdruck schreiben der direkt einen `dict()`-Aufruf mit Schlüssel/Wert-Paaren versorgt.

Andererseits stellt sich die Frage ob man hier *überhaupt* ein Wörterbuch benötigt, denn Du greifst nie über Schlüssel auf Werte zu beziehungsweise dort wo Du das machst, ist das völlig unnötig weil Du über die Schlüssel iterierst obwohl Du eigentlich die Schlüssel/Wert-Paare brauchst, also gleich darüber hättest iterieren können. Hui, und an dieser Stelle würde aus dem `result_dict`-Objekt doch tatsächlich eine *Liste* falls man den einzigen Direktzugriff auf 'ErrorCode' nicht machen müsste. ;-)

Ich lande dann als Zwischenergebnis ungefähr hier:

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf-8
import xml.etree.ElementTree as ET 
import xmlrpclib
import gtk

SERVER_URL = 'https://evatr.bff-online.de/'

# 
# TODO Beim erneuten prüfen sollte die Ergebnisausgabe zuerst gelöscht werden
# damit man sieht, dass die Ausgabe neu ist.
# 

ERROR_CODES = {
    200: 'Die angefragte USt-IdNr. ist gültig.',
    201: 'Die angefragte USt-IdNr. ist ungültig.',
    202: 'Die angefragte USt-IdNr. ist ungültig. Sie ist nicht in der Unternehmerdatei des betreffenden EU-Mitgliedstaates registriert.\nHinweis:\nIhr Geschäftspartner kann seine gültige USt-IdNr. bei der für ihn zuständigen Finanzbehörde in Erfahrung bringen. Möglicherweise muss er einen Antrag stellen, damit seine USt-IdNr. in die Datenbank aufgenommen wird.',
    203: "Die angefragte USt-IdNr. ist ungültig. Sie ist erst ab dem ... gültig (siehe Feld 'Gueltig_ab').",
    204: "Die angefragte USt-IdNr. ist ungültig. Sie war im Zeitraum von ... bis ... gültig (siehe Feld 'Gueltig_ab' und 'Gueltig_bis').",
    205: 'Ihre Anfrage kann derzeit durch den angefragten EU-Mitgliedstaat oder aus anderen Gründen nicht beantwortet werden. Bitte versuchen Sie es später noch einmal. Bei wiederholten Problemen wenden Sie sich bitte an das Bundeszentralamt für Steuern - Dienstsitz Saarlouis.',
    206: 'Ihre deutsche USt-IdNr. ist ungültig. Eine Bestätigungsanfrage ist daher nicht möglich. Den Grund hierfür können Sie beim Bundeszentralamt für Steuern - Dienstsitz Saarlouis - erfragen.',
    207: 'Ihnen wurde die deutsche USt-IdNr. ausschliesslich zu Zwecken der Besteuerung des innergemeinschaftlichen Erwerbs erteilt. Sie sind somit nicht berechtigt, Bestätigungsanfragen zu stellen.',
    208: 'Für die von Ihnen angefragte USt-IdNr. läuft gerade eine Anfrage von einem anderen Nutzer. Eine Bearbeitung ist daher nicht möglich. Bitte versuchen Sie es später noch einmal.',
    209: 'Die angefragte USt-IdNr. ist ungültig. Sie entspricht nicht dem Aufbau der für diesen EU-Mitgliedstaat gilt. ( Aufbau der USt-IdNr. aller EU-Länder)',
    210: 'Die angefragte USt-IdNr. ist ungültig. Sie entspricht nicht den Prüfziffernregeln die für diesen EU-Mitgliedstaat gelten.',
    211: 'Die angefragte USt-IdNr. ist ungültig. Sie enthält unzulässige Zeichen.',
    212: 'Die angefragte USt-IdNr. ist ungültig. Sie enthält ein unzulässiges Länderkennzeichen.',
    213: 'Die Abfrage einer deutschen USt-IdNr. ist nicht möglich.',
    214: "Ihre deutsche USt-IdNr. ist fehlerhaft. Sie beginnt mit 'DE' gefolgt von 9 Ziffern.",
    215: 'Ihre Anfrage enthält nicht alle notwendigen Angaben für eine einfache Bestätigungsanfrage (Ihre deutsche USt-IdNr. und die ausl. USt-IdNr.).\nIhre Anfrage kann deshalb nicht bearbeitet werden.',
    216: 'Ihre Anfrage enthält nicht alle notwendigen Angaben für eine qualifizierte Bestätigungsanfrage (Ihre deutsche USt-IdNr., die ausl. USt-IdNr., Firmenname einschl. Rechtsform und Ort).\nEs wurde eine einfache Bestätigungsanfrage durchgeführt mit folgenden Ergebnis:\nDie angefragte USt-IdNr. ist gültig.',
    217: 'Bei der Verarbeitung der Daten aus dem angefragten EU-Mitgliedstaat ist ein Fehler aufgetreten. Ihre Anfrage kann deshalb nicht bearbeitet werden.',
    218: 'Eine qualifizierte Bestätigung ist zur Zeit nicht möglich. Es wurde eine einfache Bestätigungsanfrage mit folgendem Ergebnis durchgeführt:\nDie angefragte USt-IdNr. ist gültig.',
    219: 'Bei der Durchführung der qualifizierten Bestätigungsanfrage ist ein Fehler aufgetreten. Es wurde eine einfache Bestätigungsanfrage mit folgendem Ergebnis durchgeführt:\nDie angefragte USt-IdNr. ist gültig.',
    220: 'Bei der Anforderung der amtlichen Bestätigungsmitteilung ist ein Fehler aufgetreten. Sie werden kein Schreiben erhalten.',
    999: 'Eine Bearbeitung Ihrer Anfrage ist zurzeit nicht möglich. Bitte versuchen Sie es später noch einmal.'
}
 
YELLOW = "#FFFF66"
GREEN = "#66FF66"
RED = "#ff8080"
ORANGE = "#FFAD66"


def create_entry(box_layout, label_text):
    layout = gtk.HBox(homogeneous=True)
    layout.pack_start(gtk.Label(label_text))
    entry = gtk.Entry()
    layout.pack_start(entry)
    box_layout.pack_start(layout, expand=False, fill=False)
    return entry


class CheckVAT(object):

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

        # define main window
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.set_position(gtk.WIN_POS_CENTER)
        self.window.set_size_request(600, 500)
        self.window.set_title('Check VAT CODEs')
       
        # HBox 1 - own data
        own_info_layout = gtk.VBox()
        own_info_layout.pack_start(
            gtk.Label('Own Data:'), expand=False, fill=False
        )
        self.self_vat_entry = create_entry(
            own_info_layout, 'Own VAT Code (necessary)'
        )
 
        # HBox 2 - external company
        other_info_layout = gtk.VBox()
        other_info_layout.pack_start(
            gtk.Label('Other Company Data:'), expand=False, fill=False
        )
        self.external_vat_entry = create_entry(
            other_info_layout, 'VAT Code (necessary)'
        )
        self.external_name_entry = create_entry(
            other_info_layout, 'Name (extended)'
        )
        self.external_city_entry = create_entry(
            other_info_layout, 'City (extended)'
        )
        self.external_zip_code_entry = create_entry(
            other_info_layout, 'ZIP Code (optional)'
        )
        self.external_street_entry = create_entry(
            other_info_layout, 'Street (optional)'
        )

        # create buttons
        button_layout = gtk.HBox()
        check_button = gtk.Button('Check')
        check_button.connect('clicked', self.check)
        button_layout.pack_start(check_button)
        self.checkbutton_print = gtk.CheckButton(
            'Request Confirmation via Letter'
        )
        button_layout.pack_start(self.checkbutton_print)
        exit_button = gtk.Button('Exit')
        exit_button.connect('clicked', self.destroy)
        button_layout.pack_start(exit_button)
       
        # create result box
        result_layout = gtk.VBox()  
        result_layout.pack_start(
            gtk.Label('Results:'), expand=False, fill=False
        )
        self.content_textbuffer = gtk.TextBuffer()
        self.content_textbuffer.set_text('…')
        self.content_textview = gtk.TextView(buffer=self.content_textbuffer)
        self.content_textview.modify_base(
            gtk.STATE_NORMAL, gtk.gdk.color_parse(YELLOW)
        )
        self.content_textview.set_wrap_mode('GTK_WRAP_WORD')
        result_layout.pack_start(self.content_textview)
       
        # create info box
        result_info_layout = gtk.VBox()  
        result_info_layout.pack_start(
            gtk.Label(
                'A:\tstimmt überein\t\tB:\tstimmt nicht überein \n'
                'C:\tnicht angefragt\t\t'
                    'D:\tvom EU-Mitgliedsstaat nicht mitgeteilt'
            )
        )

        layout = gtk.VBox(spacing=8)
        layout.pack_start(own_info_layout, expand=False, fill=False)
        layout.pack_start(other_info_layout, expand=False, fill=False)
        layout.pack_start(button_layout, expand=False, fill=False)
        layout.pack_start(result_layout)
        layout.pack_start(result_info_layout, expand=False, fill=False)
        self.window.add(layout)
        self.window.show_all()
        self.window.connect('destroy', self.destroy)
   
    @staticmethod
    def destroy(_widget=None, _data=None):
        gtk.main_quit()
   
    def check(self, _widget=None):
        response = ET.fromstring(
            self.server.evatrRPC(
                self.self_vat_entry.get_text(),
                self.external_vat_entry.get_text(),
                self.external_name_entry.get_text(),
                self.external_city_entry.get_text(),
                self.external_zip_code_entry.get_text(),
                self.external_street_entry.get_text(),
                'ja' if self.checkbutton_print.get_active() else 'nein'
            )
        )
        rpc_result = dict(
            [n.text for n in node.iter('string')]
            for node in response.findall('param')
        )
        print rpc_result
        error_code = int(rpc_result['ErrorCode'])
       
        if error_code == 200:
            color = GREEN
        elif error_code in [216, 219]:
            color = ORANGE
        else:
            color = RED
        self.content_textview.modify_base(
            gtk.STATE_NORMAL, gtk.gdk.color_parse(color)
        )

        self.content_textbuffer.set_text(
            '{0}\n\n\n{1}'.format(
                '\n'.join(
                    '{0}: {1}'.format(key, value)
                    for key, value in sorted(rpc_result.items())
                ),
                ERROR_CODES[error_code]   
            )
        )
 

def main():
    xmlrpclib.socket.setdefaulttimeout(5)
    checkvat = CheckVAT(xmlrpclib.Server(SERVER_URL))
    gtk.main()


if __name__ == '__main__':
    main()
Sirius3
User
Beiträge: 8804
Registriert: Sonntag 21. Oktober 2012, 17:20

Samstag 16. Mai 2015, 20:24

Solche if-elif-else-Ketten für z.B. color lassen sich doch wunderbar durch ein Wörterbuch ersetzen:

Code: Alles auswählen

ERROR_CODE_COLOR = {
    200: GREEN,
    216: ORANGE,
    219: ORANGE,
}

color = ERROR_CODE_COLOR.get(error_code, RED)
Benutzeravatar
martinjo
User
Beiträge: 181
Registriert: Dienstag 14. Juni 2011, 20:03

Sonntag 17. Mai 2015, 18:41

@BlackJack
Danke für die Hinweise, habe heute noch mal einiges überarbeitet. Hat oft ne Weile gedauert, da ich den Code auch verstehen wollte. Dabei habe ich dafür einige neue Möglichkeiten entdeckt. Unten im Text gehe ich nochmal etwas näher darauf ein.

Besonders gefallen hat mir die Möglichkeit das Dict mit einer Liste aufzubauen wie bei "rpc_result = dict...":

Code: Alles auswählen

>>> print dict(["a1", "c3", "d4"])
{'a': '1', 'c': '3', 'd': '4'}
Auch den Tipp von Sirius3, die Methode mit get und dem Default Wert zu nutzen kannte noch nicht. Verbunden mit so einem Beispiel kann man sich das auch gut einprägen. Danke.

Allgemein:
Um die Breite zu begrenzen habe ich nun Umbrüche ab 90 Zeichen. Die schließenden Klammern habe ich jedoch etwas weiter eingerückt als üblich, da es, wie ich finde, besser aussieht und leicherter zu dem eingerückten Block zuzuordnen ist.
Nur bei den ERROR_CODES habe ich das mal unterlassen, diese möchte ich in eine seperate Datei auslagern, wohl als CSV-Datei.
BlackJack hat geschrieben: @martinjo: `self_vat` ist zwar eine Konstante, daber die finde ich an sich unsinnig. Das ist ein Name für eine leere Zeichenkette, lokal in einer Methode definiert und nur *einmal* benutzt.
Habe ich angepasst, dass stand dort, da ich zum Testen die Konstanten mit Werten gefüllt habe, jedoch auch nur bei den beiden VAT-Werten.
BlackJack hat geschrieben: Wenn aus ``return_dict["ErrorCode"] in ["216","219"]`` durch ändern von Zeichenketten für Fehlercodes zu Zahlen ``error_code == [216] or error_code == [219]`` werden muss dann hast Du beim Umwandeln etwas falsch gemacht, nämlich statt die Zeichenkette in eine Zahl zu wandeln die Zeichenkette in eine Zahl zu wandeln die als einziges Element in einer Liste steht. Warum? Zumal man auch dann immer noch ``error_code in [[216], [219]]`` hätte schreiben können.

Die Einrückung beim `error_dict` ist jetzt etwas sehr hoch. Auch dort würde ich bei den normalen vier Leerzeichen pro Ebene bleiben. Man könnte sich hier auch noch überlegen an der Zeilenlänge zu arbeiten und wie schon mal angesprochen wurde Platzhalter für Werte aus der GUI einzufügen.
Habe ich soweit verbessert, bis auf die Zeilenlänge. Das mit dem Platzhalter habe ich jedoch nicht verstanden.
BlackJack hat geschrieben: Auf Modulebene sollten nur Konstanten, Funktionen, und Klassen definiert werden. `server` ist weder von der Namensschreibweise noch vom Wert her eine Konstante. Auf den Wert sollte man deshalb auch nicht einfach so aus Methoden heraus zugreifen ohne das er als Argument übergeben wurde, entweder direkt oder als Attribut des Objekts.
Danke, habe ich so übernommen.
BlackJack hat geschrieben: Das mit dem `WIN_POS_CENTER` würde ich mir gut überlegen. Wenn das jedes Programm machen würde hätte der Benutzer lauter Fenster in der Mitte des Bildschirms die übereinanderliegen und er müsste die jedes mal selber so verschieben das er alles sehen kann. Eine (möglichst) freie Stelle für ein neues Fenster zu finden ist aber schon die Aufgabe der Fensterverwaltung des Systems auf dem das Programm gestartet wird.
Danke, habe ich mir abgespeichert, auch wenn ich es bei diesem Programm so lasse.
BlackJack hat geschrieben: Das Durchnummerieren von Namen ist unschön. Man kann doch statt 1 und 2 auch ganz leicht bessere Namen für die beiden „VAT codes“ wählen.

In der `check()`-Methode werden auch die ganzen Argumente für den RPC-Aufruf an Namen gebunden obwohl die nur für den Aufruf verwendet werden.

Importe gehören an den Anfang vom Modul.
Habe ich verbessert, bis auf die Namensbindungen für die `check()`-Methode. Die behalte ich vorerst, damit ich die Übersicht nicht verliere.
BlackJack hat geschrieben: Ein Schlüssel/Wert-Paar wird in der Python-API nicht als `keyvalue` sondern als `item` bezeichnet. Allerdings kann man wenn man da grundsätzlich zwei Werte als Ergebnis erwartet auch einfach ``key, value = param_node.iter(STRING)`` schreiben um diese beiden Werte den zwei Namen zuzuordnen. Dann kann man das ganze aber noch weiter vereinfachen und einen Generatorausdruck schreiben der direkt einen `dict()`-Aufruf mit Schlüssel/Wert-Paaren versorgt.
Ist das in dem verbesserten Code nun schon ein Generatorausdruck?
BlackJack hat geschrieben: Andererseits stellt sich die Frage ob man hier *überhaupt* ein Wörterbuch benötigt, denn Du greifst nie über Schlüssel auf Werte zu beziehungsweise dort wo Du das machst, ist das völlig unnötig weil Du über die Schlüssel iterierst obwohl Du eigentlich die Schlüssel/Wert-Paare brauchst, also gleich darüber hättest iterieren können. Hui, und an dieser Stelle würde aus dem `result_dict`-Objekt doch tatsächlich eine *Liste* falls man den einzigen Direktzugriff auf 'ErrorCode' nicht machen müsste. ;-)
Ok, :-) habe ich umbenannt. Auch habe ich den Code nach deiner Vorlage angepasst, auch wenn ich ein paar Dinge in mehren Schritten mache damit es für mich übersichtlicher bleibt.

Frage zu "for key, value in sorted(rpc_result.items()". Das habe ich ergänzt damit nur die Werte die nicht None sind hinzugefügt (und damit angezeigt) werden. Doch so habe ich in der Liste die angezeigt wird auch den "ErrorCode" mit Wert welcher verwirrend sein kann und unnötig ist. Wie bekomme ich diesen am besten aus der Liste? Das folgende funktioniert leider nicht:

Code: Alles auswählen

 for key, value in sorted(rpc_result.items()) if value and key is not "ErrorCode"


Anbei noch der neue Code:

Code: Alles auswählen

#!/usr/bin/env python
#-*- coding: utf-8 -*-

import gtk
import xmlrpclib
import xml.etree.ElementTree as ET 

SERVER_URL = 'https://evatr.bff-online.de/'

"""
Über diese Schnittstelle können Sie sich täglich, in der Zeit zwischen 05:00 Uhr
und 23:00 Uhr, die Gültigkeit einer ausländischen Umsatzsteuer-Identifikations-
nummer (USt-IdNr.) bestätigen lassen. 

Felder mit "necessary" sind auszufüllen für eine einfache Rückmeldung
Felder mit "extended" sind auszufüllen für eine qualifizierte Rückmeldung
Felder mit "optional" find auszufüllen für eine erweiterte, qualifizierte Rückmeldung

weitere Infos:
https://evatr.bff-online.de/eVatR/xmlrpc/python
https://evatr.bff-online.de/eVatR/xmlrpc/aufbau
https://evatr.bff-online.de/eVatR/xmlrpc/codes
"""

ERROR_CODES = {
    200 : "Die angefragte USt-IdNr. ist gültig.",
    201 : "Die angefragte USt-IdNr. ist ungültig.",
    202 : "Die angefragte USt-IdNr. ist ungültig. Sie ist nicht in der Unternehmerdatei des betreffenden EU-Mitgliedstaates registriert.\nHinweis:\nIhr Geschäftspartner kann seine gültige USt-IdNr. bei der für ihn zuständigen Finanzbehörde in Erfahrung bringen. Möglicherweise muss er einen Antrag stellen, damit seine USt-IdNr. in die Datenbank aufgenommen wird.",
    203 : "Die angefragte USt-IdNr. ist ungültig. Sie ist erst ab dem ... gültig (siehe Feld 'Gueltig_ab').",
    204 : "Die angefragte USt-IdNr. ist ungültig. Sie war im Zeitraum von ... bis ... gültig (siehe Feld 'Gueltig_ab' und 'Gueltig_bis').",
    205 : "Ihre Anfrage kann derzeit durch den angefragten EU-Mitgliedstaat oder aus anderen Gründen nicht beantwortet werden. Bitte versuchen Sie es später noch einmal. Bei wiederholten Problemen wenden Sie sich bitte an das Bundeszentralamt für Steuern - Dienstsitz Saarlouis.",
    206 : "Ihre deutsche USt-IdNr. ist ungültig. Eine Bestätigungsanfrage ist daher nicht möglich. Den Grund hierfür können Sie beim Bundeszentralamt für Steuern - Dienstsitz Saarlouis - erfragen.",
    207 : "Ihnen wurde die deutsche USt-IdNr. ausschliesslich zu Zwecken der Besteuerung des innergemeinschaftlichen Erwerbs erteilt. Sie sind somit nicht berechtigt, Bestätigungsanfragen zu stellen.",
    208 : "Für die von Ihnen angefragte USt-IdNr. läuft gerade eine Anfrage von einem anderen Nutzer. Eine Bearbeitung ist daher nicht möglich. Bitte versuchen Sie es später noch einmal.",
    209 : "Die angefragte USt-IdNr. ist ungültig. Sie entspricht nicht dem Aufbau der \
für diesen EU-Mitgliedstaat gilt. ( Aufbau der USt-IdNr. aller EU-Länder)",
    210 : "Die angefragte USt-IdNr. ist ungültig. Sie entspricht nicht den Prüfziffernregeln die für diesen EU-Mitgliedstaat gelten.",
    211 : "Die angefragte USt-IdNr. ist ungültig. Sie enthält unzulässige Zeichen.",
    212 : "Die angefragte USt-IdNr. ist ungültig. Sie enthält ein unzulässiges Länderkennzeichen.",
    213 : "Die Abfrage einer deutschen USt-IdNr. ist nicht möglich.",
    214 : "Ihre deutsche USt-IdNr. ist fehlerhaft. Sie beginnt mit 'DE' gefolgt von 9 Ziffern.",
    215 : "Ihre Anfrage enthält nicht alle notwendigen Angaben für eine einfache Bestätigungsanfrage (Ihre deutsche USt-IdNr. und die ausl. USt-IdNr.).\nIhre Anfrage kann deshalb nicht bearbeitet werden.",
    216 : "Ihre Anfrage enthält nicht alle notwendigen Angaben für eine qualifizierte Bestätigungsanfrage (Ihre deutsche USt-IdNr., die ausl. USt-IdNr., Firmenname einschl. Rechtsform und Ort).\nEs wurde eine einfache Bestätigungsanfrage durchgeführt mit folgenden Ergebnis:\nDie angefragte USt-IdNr. ist gültig.",
    217 : "Bei der Verarbeitung der Daten aus dem angefragten EU-Mitgliedstaat ist ein Fehler aufgetreten. Ihre Anfrage kann deshalb nicht bearbeitet werden.",
    218 : "Eine qualifizierte Bestätigung ist zur Zeit nicht möglich. Es wurde eine einfache Bestätigungsanfrage mit folgendem Ergebnis durchgeführt:\nDie angefragte USt-IdNr. ist gültig.",
    219 : "Bei der Durchführung der qualifizierten Bestätigungsanfrage ist ein Fehler aufgetreten. Es wurde eine einfache Bestätigungsanfrage mit folgendem Ergebnis durchgeführt:\nDie angefragte USt-IdNr. ist gültig.",
    220 : "Bei der Anforderung der amtlichen Bestätigungsmitteilung ist ein Fehler aufgetreten. Sie werden kein Schreiben erhalten.",
    221 : "Die Anfragedaten enthalten nicht alle notwendigen Parameter oder einen ungültigen Datentyp.",
    999 : "Eine Bearbeitung Ihrer Anfrage ist zurzeit nicht möglich. Bitte versuchen Sie es später noch einmal."
    }

YELLOW = "#FFFF66"
GREEN = "#66FF66"
RED = "#ff8080"
ORANGE = "#FFAD66"


def create_entry( box_layout, label_text ):
    layout = gtk.HBox( homogeneous=True )
    layout.pack_start( gtk.Label( label_text ) )
    entry = gtk.Entry()
    layout.pack_start( entry )
    box_layout.pack_start( layout, expand=False, fill=False )
    return entry


class CheckVAT(object):
    
    def __init__( self, server ):
        self.server = server
        
        # define main window
        self.window = gtk.Window( gtk.WINDOW_TOPLEVEL )
        self.window.set_position( gtk.WIN_POS_CENTER )
        self.window.set_size_request( 650, 550 )
        self.window.set_title( "Check VAT CODEs" )
        
        # own data box
        own_info_layout = gtk.VBox()
        own_info_layout.pack_start(
            gtk.Label( 'Own Data:' ), expand=False, fill=False
            )
        self.self_vat_entry = create_entry(
            own_info_layout, 'Own VAT Code (necessary)'
            )
        self_vat_box = gtk.HBox(homogeneous=True)
        self.self_vat_entry.set_text( '' )
        
        
        # external company data box
        other_info_layout = gtk.VBox()
        other_info_layout.pack_start(
            gtk.Label('Other Company Data:'), expand=False, fill=False
            )
        # vat
        self.external_vat_entry = create_entry(
            other_info_layout, 'VAT Code (necessary)'
            )
        self.external_vat_entry.set_text( '' )
        # name
        self.external_name_entry = create_entry(
            other_info_layout, 'Name (extended)'
            )
        # city
        self.external_city_entry = create_entry(
            other_info_layout, 'City (extended)'
            )
        # zip code
        self.external_zip_code_entry = create_entry(
            other_info_layout, 'ZIP Code (optional)'
            )
        # street
        self.external_street_entry = create_entry(
            other_info_layout, 'Street (optional)'
            )
        
        # create buttons
        button_layout = gtk.HBox()
        check_button = gtk.Button( "Check" )
        check_button.connect( "clicked", self.check )
        button_layout.pack_start( check_button )
        self.checkbutton_print = gtk.CheckButton( "Request Confirmation via Letter" )
        button_layout.pack_start( self.checkbutton_print )
        exit_button = gtk.Button( "Exit" )
        exit_button.connect( "clicked", self.destroy )
        button_layout.pack_start( exit_button )
        
        # create result box
        result_layout = gtk.VBox()   
        result_layout.pack_start( gtk.Label( "Results:" ), expand = False, fill = False)
        
        self.content_textbuffer = gtk.TextBuffer()
        self.content_textbuffer.set_text( "..." )
        self.content_textview = gtk.TextView( buffer=self.content_textbuffer )
        self.content_textview.modify_base( 
            gtk.STATE_NORMAL, gtk.gdk.color_parse( YELLOW ) 
            )
        self.content_textview.set_wrap_mode( "GTK_WRAP_WORD" )
        result_layout.pack_start( self.content_textview )
        
        # create info box
        result_info = gtk.VBox()   
        result_info.pack_start(
            gtk.Label( " A:\tstimmt überein \t\t B:\tstimmt nicht überein"
                "\n C:\tnicht angefragt \t\t D:\tvom EU-Mitgliedsstaat nicht mitgeteilt"
                )
            )
        
        # set up boxes and content
        layout = gtk.VBox( spacing=8 )
        layout.pack_start( own_info_layout, expand = False, fill = False )
        layout.pack_start( other_info_layout, expand = False, fill = False )
        layout.pack_start( button_layout, expand = False, fill = False )
        layout.pack_start( result_layout )
        layout.pack_start( result_info, expand = False, fill = False )
        self.window.add( layout )
        self.window.show_all()
        self.window.connect( "destroy", self.destroy )


    # define close
    @staticmethod
    def destroy( _widget, _data=None ):
        gtk.main_quit()
    
    def check( self, _widget=None ):

        # necessary values for simple confirmation
        ustid_1 = self.self_vat_entry.get_text()
        ustid_2 = self.external_vat_entry.get_text()

        # necessary values for qualified confirmation
        firmenname = self.external_name_entry.get_text()
        ort = self.external_city_entry.get_text()
        
        # optional values for qualified confirmation
        plz = self.external_zip_code_entry.get_text()
        strasse = self.external_street_entry.get_text()
        drucken = "ja" if self.checkbutton_print.get_active() else "nein"
        
        # build RPC request
        rpc = self.server.evatrRPC(
            ustid_1, ustid_2, firmenname, ort, plz, strasse, drucken
            )

        # get result
        response = ET.fromstring( rpc )
        
        # save response results
        PARAM = "param"
        STRING = "string"
        ERRORCODE = "ErrorCode"

        rpc_result = dict(
            [ n.text for n in node.iter( STRING ) ]
            for node in response.findall( PARAM )
            )
            
        error_code = int( rpc_result[ ERRORCODE ] )
        
        # color output view background
        ERROR_CODE_COLOR = { 200: GREEN, 216: ORANGE, 219: ORANGE }
        color = ERROR_CODE_COLOR.get( error_code, RED )
        self.content_textview.modify_base( 
            gtk.STATE_NORMAL, gtk.gdk.color_parse( color )
            )
             
        # create and show result output text
        view_data = "\n".join(
            '{0}: {1}'.format(key, value)
            for key, value in sorted(rpc_result.items()) if value
            )
        response_type = ERROR_CODES[ error_code ]
        result_view_text = '{0}\n\n{1}'.format( view_data, response_type )
        self.content_textbuffer.set_text( result_view_text )


def main():
    xmlrpclib.socket.setdefaulttimeout( 5 )
    checkvat = CheckVAT( xmlrpclib.Server( SERVER_URL ) )
    gtk.main()

if __name__ == "__main__":
    main()
Antworten