Programm zur automatischen Rechnungskontrolle - welches Importformat?

  • VB.NET
  • .NET 4.5

Es gibt 24 Antworten in diesem Thema. Der letzte Beitrag () ist von DerSmurf.

    Programm zur automatischen Rechnungskontrolle - welches Importformat?

    Hallo ihr lieben
    Ich mache mich gerade an den nächsten Teil meines Programmes. Die automatische Rechnungskontrolle.
    Da ich hier aber sehr viel Googlen muss, bitte ich um Hilfe bei den Vorüberlegungen.

    So funktioniert das Programm bisher (in meiner VBA Excel Variante).
    Ich konvertiere eine pdf Rechnung, mithilfe des Abby Fine Readers, in eine xlsx Datei.
    Diese Importiere ich dann in mein Programm - es öffnet sich eine UserForm in der der User die Firma angeben muss.
    Gibt es zu dieser Firma Standartwerte, werden diese geladen und in meiner Tabelle oberhalb der Rechnung angezeigt.
    Standartwerte meint: Spalte für Art.Nr (z.B. A), Spalte für Art. Name (B), Spalte für Preis (C), muss gerechnet werden (boolean), ggf. Spalte für Rabatt
    Nun kann der User diese Standartspalten verändern, wenn z.B. das Blatt schief eingescannt wurde und die Art Nr. nicht - wie üblich - in Spalte A stehen, kann er halt B hinschrieben.
    Außerdem ist es natürlich möglich die Einträge innerhalb der Tabelle (also der xlsRechnungsImport) zu verschieben - falls Blatt 1 die ArtNr in A hat, und Blatt 2 in B.

    Nach einem Klick auf Kontrolle wird nun der Rechnungsimport in ein Array geschmissen und eine LIste der Soll Preise (export aus Warenwirtschaftssystem (WWS)), wird ebenfalls in ein Array geschmissen (dies erhöht in VBA die Performance DEUTLICH - im Vergleich zum Arbeiten "auf dem Blatt")
    Es wird nun jede Art.Nr. der Rechnung, mit den Artikelnummern aus der WWS verglichen.
    Wenn eine Art.Nr. gefunden wurde, wird der Preis (entweder Listenpreis, oder wenn gerechnet werden soll, der Listenpreis abzgl. Rabatt) verglichen.
    Ist er gleich wird an die Zeile im Array ein "korrekt" angehängt.
    Ist der Preis falsch (egal ob zu teuer oder zu billig), wird die Differenz angehängt.
    Wurde eine Artikelnummer von der Rechnung nicht gefunden, wird "nicht gefunden" angehängt.
    Sind alle Artiklenummern durchgerattert, wird das Array zurück ins Blatt geschrieben.
    So dass dann jeder Artikel gekennzeichnet ist ob der Preis korrekt oder falsch ist, oder ob der Artikel nicht gefunden wurde.
    Danach gibt es noch Zusatzfunktionen um z.B. alle korrekten Preise, oder alle nicht gefundenen Artikel zu entfernen.

    Dies möchte ich nun in Vb.Net umsetzen.
    Ich denke dafür werde ich weiterhin den Abby FIne Reader verwenden - denn das Programm funktioniert ja einwandfrei und es ist eben vorhanden.
    Trotzdem wäre mir eine kostenlose Alternative lieber - aber ich bezweifel dass ich diese finden werde.
    Der Fine Reader unterstützt die OCR Erkennung in folgende Formate:
    rtf / pdf / doc / odt / pptx / html / txt / xls / csv / epub / fb2 / djvu

    Ich würde hier csv wählen und dieses dann in ein Datagridview importieren.
    Die Standartwerte für Firmen speichere ich in einem DataSet (hier gibt es bereits eine DataTable für Lieferanten).
    Diese Standartwerte lasse ich in Text (oder Comboboxen) überm Dgv anzeigen, genau wie eine Checkbox "bolrechnen".
    Dann kann ich genau wie in meinem Excel Programm die Spalten in denen die Werte stehen auswhälen und ggf. im Dgv die Daten hin und her schieben (wie in Excel halt).

    Würdet ihr auch diesen Ansatz wählen - oder einen anderen?
    Kein csv, sondern was anderers? Kein DGV sondern was andereres? etc?

    DerSmurf schrieb:

    Ich denke dafür werde ich weiterhin den Abby FIne Reader verwenden - denn das Programm funktioniert ja einwandfrei und es ist eben vorhanden.
    Trotzdem wäre mir eine kostenlose Alternative lieber - aber ich bezweifel dass ich diese finden werde.
    Das hängt sehr von der PDF ab.
    Ist das eine programmerzeugte PDF oder ein Scan?
    Wenn du kein OCR benötigst, ist es kein Problem, das mit iTextSharp (oder besser dessen Nachfolger iText 7) zu machen. -> nuget

    Wenn du Texterkennung benötigst, könnte Pdf.Ocr einen Versuch wert sein.
    Falls es je zu kompliziert wird, kannst du ja immer noch die bewährte bestehende Variante einsetzen.

    Ich würde auf jeden Fall mit dem Import der Daten beginnen und dafür eine geeignete Datenstruktur schaffen.
    Wie du das dann weiterverarbeitest oder anzeigst ist erst mal schnuppe.
    --
    If Not Program.isWorking Then Code.Debug Else Code.DoNotTouch
    --
    Huhu.
    Es handelt sich um beides - Programmerzeugte und eingescannte Pdfs.
    Aktuell kommt das meiste aus meinem Scanner - wobei das ja in Zukunft immer weniger werden wird.
    Also brauche ich aktuell noch OCR - Pdf.Ocr werde ich mir mal ansehen, hab ich noch nie gehört.

    petaod schrieb:

    Ich würde auf jeden Fall mit dem Import der Daten beginnen und dafür eine geeignete Datenstruktur schaffen.

    Ist denn csv in Verbindung mit Datagridview eine geeignete Datenstruktur? Oder gibt es hier sinnvollere?
    Nun, in nem tDS-kompatiblen Format wirst Du es wohl nicht bekommen. Daher sieht CSV immer noch am besten/einfachsten aus. DIe anderen (von TXT mal abgesehen) sind zweifellos mit viel Zusatzgeräusch, daher ist ein CSV-Import wohl am einfachsten. Musst mal den Vergleich machen, was strukturell besser ist: CSV oder TXT.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Häufig von mir verwendete Abkürzungen: CEs = control elements (Labels, Buttons, DGVs, ...) und tDS (typisiertes DataSet)
    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht in den Spekulatiusmodus gehen.

    DerSmurf schrieb:

    Ist denn csv in Verbindung mit Datagridview eine geeignete Datenstruktur?
    Nein.
    CSV ist ein Dateiformat und DGV ist eine Anzeigewerkzeug.

    Was du benötigst ist ein Datenmodell in deinem Programm selbst.
    Im einfachen Fall kann das ein Dataset sein, das du per DataAdapter befüllst und zur Anzeige an ein DGV bindest.
    --
    If Not Program.isWorking Then Code.Debug Else Code.DoNotTouch
    --
    ZUGFERD (quasi im PDF eingebettete xml Datei) wird sich in Zukunft durchsetzen. Demnächst bei Kommunen Pflicht beim elektronischen Rechnungen.

    ferd-net.de/zugferd/definition/was-ist-zugferd.html
    "Gib einem Mann einen Fisch und du ernährst ihn für einen Tag. Lehre einen Mann zu fischen und du ernährst ihn für sein Leben."

    Wie debugge ich richtig? => Debuggen, Fehler finden und beseitigen
    Wie man VisualStudio nutzt? => VisualStudio richtig nutzen
    Naja, ich stelle mir vor, dass ich dann in Zukunft keine reinen pdf Rechnungen mehr bekomme, sondern eben einen Hybriden aus pdf und xml.
    Die eingebettete xml könnte ich ja dann sehr viel einfacher einlesen als eine OCR konvertierte (in welches Format auch immer) pdf.

    Aber ich mag nicht so lange warten.

    petaod schrieb:

    Im einfachen Fall kann das ein Dataset sein, das du per DataAdapter befüllst und zur Anzeige an ein DGV bindest.

    Das heißt du würdest nicht mit den Daten aus dem DGV arbeiten, sondern diese vor dem Vergleichen in einem DataSet unterbringen?
    Also muss ich meine csv (ich gehe immernoch davon aus, das dies das geeignetste Abby Fine Reader Eport Format ist) in ein DGV importieren, und die benötigten Spalten in ein DataSet schmeißen.
    Damit ich dann das "RechnungsDataSet" mit dem "ArtikelDataSet" vergleichen kann.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „DerSmurf“ ()

    Ohne DGV dazwischen. Die Daten in der CSV kannst Du doch super parsen. Erst eine Datenzeile am Delimiter splitten, dann alles einzeln per Date.Parse, Integer.Parse und Co. direkt in die DataTable.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Häufig von mir verwendete Abkürzungen: CEs = control elements (Labels, Buttons, DGVs, ...) und tDS (typisiertes DataSet)
    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht in den Spekulatiusmodus gehen.

    DerSmurf schrieb:

    Also rein ins DGV, Spalten auswählen und ab ins DataSet damit
    Wir verstehen uns nicht. Entweder ich dich nicht oder du mich nicht.

    Daten rein in ein Dataset oder eine DataTable.
    Das DGV ist, wenn man es überhaupt braucht, ans Dataset gebunden und dient zur Anzeige (und ggf. manuellen Veränderung).
    Programmtechnisch gearbeitet wird mit den Daten im Dataset.
    --
    If Not Program.isWorking Then Code.Debug Else Code.DoNotTouch
    --
    Ja kann ich.
    Das Problem ist nur, dass ich erst nach dem Import der csv weiß, welche Daten wo stehen.

    Also wenn wir uns die csv als Tabelle denken, weiß ich ja vorher nicht, in welcher Spalte die Daten stehen.
    Ist ja bei jeder Rechnung (bzw. bei jeder Firma) anders.
    Mal ist die Artikelnummer in Spalte 1, mal in Spalte 2.
    Mal steht der Artikelname in Spalte 4, mal in Spalte 5.
    Ohne dies zu wissen, weiß ich doch nicht, was ich wo in die DataTable schmeißen soll. Also welcher Eintrag z.B. die Art.Nr. ist.

    Also habe ich mir den Umweg des "ErstInDgvImportieren", dann Spalen auswählen, und dann ab in die DataTable ausgedacht.
    Oder Du machst einen firmenabhängigen Datenimport. Entweder der Name der Datei oder der Inhalt wird ja wohl auf die Firma hinweisen, oder?
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Häufig von mir verwendete Abkürzungen: CEs = control elements (Labels, Buttons, DGVs, ...) und tDS (typisiertes DataSet)
    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht in den Spekulatiusmodus gehen.
    Das wäre realisierbar - aber schwierig.
    Der Dateiname ist sinnlos.
    Ich konvertiere mit dem Fine Reader in der Regel mehrere Dateien gleichzeitig.
    Dann werden csv mit den gleichen Dateinamen wie die Originaldateien erstellt. Die Originaldateinamen sind aber auch nicht zu gebrauchen. Das immer umzubennen ist mir zu lästig.
    In der csv selber steht sicherlich irgendwo der Firmenname. Allerdings wird dieser vom FineReader nicht immer korrekt in Text umgewandelt, da der Name manchmal sehr groß, oder generell anders als der "normale" Text geschrieben ist.
    Ich könnte also Zeichen für Zeichen lesen, bis irgendwann eine Firma gefunden wurde. (Vergleich der Zeichen mit DataSetArticle.DataTableSupplier).

    Nun ist es aber so, dass auch die Spalten bei einer Firma leider nicht immer gleich sind.
    Je nachdem wie gerade ich die Rechnung einscanne, varriiert das ganze - teilweise auch innerhalb einer einzigen Rechnung.
    Also auf Seite 1 ist die Artikelnummer in Spalte 1, auf Seite 2 in Spalte 2 (weil Seite 2 schief gescannt.
    Ja gut, dann sehe ich da auch keinen besseren Weg als alles erstmal in ein DGV und dann von dort in die DataTable. :(
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Häufig von mir verwendete Abkürzungen: CEs = control elements (Labels, Buttons, DGVs, ...) und tDS (typisiertes DataSet)
    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht in den Spekulatiusmodus gehen.

    petaod schrieb:

    Daten rein in ein Dataset oder eine DataTable.

    DerSmurf schrieb:

    Das Problem ist nur, dass ich erst nach dem Import der csv weiß, welche Daten wo stehen.
    Jo, da muss man dann herumhumpsen.
    Aber das musste so oder so.
    Wenns keine Verbindlichkeit gibt, welche Daten in welcher Spalte stehen, muss man an der Stelle Aufwand treiben.
    Also hier Beispiel, wenn die Verbindlichkeit besteht, also Csv-Import direkt in die DataTable eines TypDatasets: Csv importieren
    Ansonsten haste wohl recht: Da muss man dann einen Dialog zwischenschalten, wo man iwie festlegen kann, welche Spalte wohin in die DataTable gehört.
    Ja so ist der Plan.
    Ich lade erstmal die csv ins DGV.
    Dann kann der User eine Firma auswählen und mittels Comboboxen, in welche dann Default Werte geladen werden, angeben was wo steht. Also Art.Nr. Spalte 1, Name Spalte 2 usw.
    Anhnand dieser Daten kann ich dann den Inhalt des DGV in eine DataTable schmeißen und diese dann typisiert verarbeiten.

    Ich tue mich gerade schwer eine inhaltlich völlig unbekannte csv gescheit zu laden - aber lasst mich mal machen.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „DerSmurf“ ()

    Soho. Ich glaubs selber noch nciht ganz, aber ich habe es hinbekommen.
    Der Import von csv mit unbekanntem Inhalt ins DGV scheint zu funktionieren und auch das korrekte schreiben der Daten ins DataSet klappt.

    Aber eine Frage habe ich noch.
    Ich habe zwei DataSets. Das erste "DtsArticle" speichert in zwei DataTable die Artikel (Article) und dazugehörige Lieferanten (Supplier).
    In Article gibt es eine Spalte SupplierID - mit entsprechendem Verweis.
    DataSet Nummer zwei (InvoiceControl) hat nur ein DataTable und dient dem csv Import.
    Hier gibt es folgende Spalten:
    ID
    ArtNR
    Name
    Listenpreis
    Rabatt
    Preis (=Listenpreis - Rabatt)
    SavedPreis (=Preis aus DtsArticle.Article)
    Differenz (=Preis - SavedPreis)
    Result ("Preis korrekt", "Anzeige Differenz", oder "nicht gefunden")

    Nach dem Import sind alle Spalten von InvoiceControl korrekt gefülllt.
    Außer SavedPreis / Differenz / und Result
    Um SavedPreis zu füllen muss ich DtsArticle.Article mit einer Schleife durchlaufen, bis = DtsArticle.Article.ArtNr = InvoiceControl.ArtNr und habe dann den entsprechenden Preis.
    Das wäre kein Problem.
    Nun stellt sich mir aber die Frage, wie ich diese Schleife nur durch die Daten des ausgewählten Lieferanten laufen lassen kann.
    Das würde die Sache ja ungemein beschleunigen.
    Die SupplierBindingSource hat zu Beginn der Sub bereits einen .Current Wert, da vor dem Start mittels Combobox ein Lieferant (Dts.Article.Supplier) ausgewählt werden muss - um die "Spalten DefaultWerte" zu laden.
    Es ist also die entsprechende SupplierID bekannt und auch ausgewählt.
    Wie muss ich meine Schleife aufsetzen, damit eben nur DataSet Einträge zu dieser SupplierID durchlaufen werden?