0

Rechnungsstatus nach Abgleich mit Bankingdaten ändern

Hallo zusammen,

wahrscheinlich stehe ich einfach nur sehr stark auf dem Schlauch, aber ich hoffe, dass ihr mir helfen könnt: Wir laden in unsere Datenbank in eine Tabelle "Bankingdaten" regelmäßig alle Überweisungen hoch, die passiert sind. Das machen wir über einen CSV Bankauszug. Eigentlich war das nur für Eingangsrechnungen wichtig, jetzt wollen wir aber gern auch die bezahlten Ausgangsrechnungen dort abbilden.

Da kam mir der Gedanke, dass es möglich sein muss (entweder als Trigger nach neuem Datensatz/Änderung beim Hochladen oder über einen Button irgendwo), dass die Rechnungen in den Verwendungszwecken nach ihrer Rechnungsnummer suchen und sich dann selbst auf "bezahlt" setzen, wenn die Rechnungsnummer irgendwo im Verwendungszweck auftaucht. Leider hat man ja auch ständig Kunden, die alles mögliche Andere auch noch da rein schreiben, weshalb ich nicht exakt nach der Nummer suchen kann (oder doch?).

Nochmal kurz zur Struktur: In der Tabelle "Bankingdaten" ist die Spalte "Verwendungszweck", diese wird regelmäßig mit neuen Daten gefüttert. In "Rechnungen" gibt es "Rechnungsnr.", die soll gesucht werden und dann der "Status" auf 2/bezahlt gesetzt werden. Von mir aus auch über eine Untertabelle, hauptsache, es klappt irgendwie.

Ich hoffe, ihr könnt mir helfen - das Forum hat mir in der Vergangenheit schon viele nützliche Hinweise geliefert!

13 Antworten

null
    • Ninox-Professional
    • planoxpro
    • vor 10 Monaten
    • Gemeldet - anzeigen

    Hallo Linda, es gibt verschiedene Ansätze dazu. Eine Möglichkeit wäre, die offenen Rechnungen in ein Array zu lesen und die Buchungstexte dann mit contains() nach den Rechnungsnummern zu durchsuchen. Ein ganz einfaches Beispiel für einen einzelnen Datensatz der importierten CSV-Liste:

    let myOP := select Rechnungen where Status = "Offen";
    for i in myOP do
       if contains(Verwendungszweck, i.RechnungsNr) then
          i.Status := "Bezahlt"
       end
    end

    Die genaue Umsetzung hinge natürlich davon ab, was genau bei einem Treffer passieren soll (im Beispiel: Status auf „Bezahlt“ setzen).

    Wenn eure Rechnungsnummern einen festen Präfix und ein bestimmtes Schema haben, dann könnte man auch den umgekehrten Weg gehen und versuchen, mit extractx() per Regex die Rechnungsnummer und/oder die Kundennummer aus dem Verwendungszweck zu extrahieren und mit „first(select ... where ...“ in den offenen Rechnungen danach zu suchen. Ein weiteres, evtl. ergänzendes Suchkriterium könnte der Betrag sein.

    Eine 100-prozentige Zuordnung wird man wohl nie hinbekommen, aber wenn die Rechnungsnummer im Verwendungszweck enthalten ist, dann sollte es zumindest bei einem Teil der Einträge funktionieren. Wobei gilt: Je individueller die Rechnungsnummer, desto größer die Wahrscheinlichkeit, dass die Buchung sicher zugeordnet werden kann.

    Übrigens gibt es inzwischen auch eine Integration, mit der sich automatisiert Kontoauszüge holen und auch Transaktionen ausführen lassen. Ist noch im Betastadium, sieht aber schon sehr vielversprechend aus: https://kontoflux.io/

    Das könnte man täglich ausführen lassen und würde sich den ständigen manuellen CSV-Import sparen.

      • Ninox-Professional
      • planoxpro
      • vor 10 Monaten
      • Gemeldet - anzeigen

      Es ging zwar nur um die Darstellung des Prinzips, aber der Vollständigkeit halber: In der ersten Zeile müsste es natürlich heißen ... where text(Status) = "Offen".

      • Linda_Dauble
      • vor 10 Monaten
      • Gemeldet - anzeigen

      Hi planox, du hättest meinen Freudenausruf hören sollen! Ich musste zwar mit like arbeiten, weil unsere Kunden an einigen Stellen wirklich ganz besonders merkwürdig sind. Aber das führt natürlich zu dem Problem, dass auch Rechnungen, die ähnliche Rechnungsnummern haben, sich auch auf bezahlt setzen. Contains habe ich gar nicht zum Laufen bekommen (auch mit lower nicht z.B.).

      Wenn ich den Betrag in das if mit einbaue, gibt es natürlich Probleme, wenn jemand den falschen Betrag überweist (brutto statt netto, Centbeträge, usw). Hast du eine Ahnung, wieso contains nicht tut was es soll?

      Ein Präfix haben unsere Rechnungen leider nicht.

    • mirko3
    • vor 10 Monaten
    • Gemeldet - anzeigen

    Vielleicht fehlt nur ein "i" vor Verwendungszweck, was man auch ausklammern könnte vor contains?

    let myOP := select Rechnungen where Status = "Offen";
    for i in myOP do
       if contains(i.Verwendungszweck, i.RechnungsNr) then
          i.Status := "Bezahlt"
       end
    end
    
      • Ninox-Professional
      • planoxpro
      • vor 10 Monaten
      • Gemeldet - anzeigen

       Danke für deinen Hinweis, aber: Nein, das Beispiel bezog sich auf einen einzelnen Datensatz in der CSV-Tabelle, in welcher sich auch das Textfeld Verwendungszweck befindet. Über die Variable i wird auf das Array mit den Datensätzen der Tabelle Rechnungen zugegriffen.

    • Ninox-Professional
    • planoxpro
    • vor 10 Monaten
    • Gemeldet - anzeigen
     said:
    Hast du eine Ahnung, wieso contains nicht tut was es soll?

    Leider nicht. Wenn der String (RechnungsNr) exakt so in dem zu durchsuchenden Textfeld (Verwendungszweck) irgendwo vorkommt, dann wird er mit contains() auch gefunden.

    Falls die RechnungsNr Buchstaben enthält, sollte man natürlich beide Felder mit upper() oder lower() angleichen:

    contains(upper(Verwendungszweck), i.upper(RechnungsNr)

    Zusätzlich kann man mit replace() auch noch Leer- und Sonderzeichen entfernen für den Fall, dass der Kunde welche weglässt oder hinzufügt. Dann müsste man eigentlich eine recht hohe Trefferquote bekommen.

    Ansonsten fällt mir spontan nichts weiter ein. Bei mir funktioniert es so wie beschrieben.

      • Linda_Dauble
      • vor 10 Monaten
      • Gemeldet - anzeigen

      Ich hab nochmal ein bisschen rumgespielt, komme aber zu keinem Ergebnis. Deshalb gebe ich dir mal ein Beispiel, wie die Sachen aussehen, vielleicht siehst du dann das ganz offensichtliche Problem schon:
      Der Button hat diesen Code (weil auch überfällige natürlich geprüft werden müssen)

      let myOP := (select Rechnungen where Status != 2);
      for i in myOP do
          if contains(Bankingdaten.Verwendungszweck, i.'Rechnungsnr.') then
              i.(Status := 2)
          end
      end
      

      Wie gesagt, mit Like setzt es die Rechnungen auf 2, also ist die Verbindung der Tabellen wohl nicht das Problem.
      Die Rechnungsnr. ist dann z.B. 510282 und der
      Verwendungszweck / Kto. 0000064261 T11 /Ref. 510282 23.11.2023/
      Ich hab auch schon probiert, beides über text() ganz sicher zu Textfeldern zu machen (falls das CSV komische Dinge getan haben sollte oder so), aber keine Chance.
      Es tut eben einfach gar nichts. Gibt es eine Möglichkeit zu sehen, was der Code ausgespuckt hätte? Also die Bedingung ist ja offensichtlich warum auch immer nicht erfüllt, aber kann man irgendwo schauen, welchen Fehler ninox findet?

      Danke auf jeden Fall schonmal für die Hilfe bis hierher :)

      • Ninox-Professional
      • planoxpro
      • vor 10 Monaten
      • Gemeldet - anzeigen

       

      Hallo Linda, mein einfaches Code-Beispiel war ursprünglich dafür gedacht, in einem (einzelnen) Datensatz der Tabelle mit den importierten CSV-Buchungen ausgeführt zu werden, um erst mal nur die Wirksamkeit des Abgleichs per contains() testen zu können.

      Die Schreibweise „contains(Bankingdaten.Verwendungszweck ...“ in deinem Code legt nahe, dass er in einer verknüpften Tabelle ausgeführt wird. In welcher: Rechnungen? Wie sind die Tabellen miteinander verknüpft? Sollen auch die Datensätze miteinander verknüpft werden (Rechnung mit Zahlung/en)? Und wie willst du grundsätzlich vorgehen: Für eine Rechnung prüfen, ob ein Zahlungseingang dafür vorliegt? Oder umgekehrt, ob zu einem Zahlungseingang eine Rechnung vorliegt?

      Geht sicher beides, aber die Umsetzung wäre unterschiedlich. Wenn der Code in der Tabelle Rechnungen ausgeführt werden soll, dann würde ich es mal so versuchen (ohne Berücksichtigung irgendwelcher Verknüpfungen):

      let me := this;
      for i in select Bankingdaten do
         if contains(i.Verwendungszweck, me.'Rechnungsnr.) then
            me.(Status := 2)
         end
      end

      PS: Falls du damit nicht weiterkommen solltest, könntest du auch gerne deine Datenbank hier einstellen (beteiligte Tabellen mit jeweils ein, zwei Demodatensätzen). Dann könnte man mal direkt reinschauen.

      • Linda_Dauble
      • vor 10 Monaten
      • Gemeldet - anzeigen

      Okay, das ergibt soweit erstmal Sinn. Ich wollte das im Dashboard ausführen, der Einfachheit halber. Für die Verknüpfungsfrage musste ich das einmal nachschauen, sind aber alles zu-Beziehungen. Brauch ich da vielleicht von-Beziehungen irgendwo? Ich will, dass alle Rechnungen gleichzeitig geprüft werden. Dein Code oben funktioniert aber fantastisch für einzelne Rechnungen, das ist ja schonmal was (mit Button in der Rechnung).

      Die Rechnungen gibt es vorher. Am besten wäre es, wenn ich schauen könnte, ob für die Rechnung ein Zahlungseingang vorliegt. Verknüpfen brauch ich die nicht.

      Geht das aus dem Dashboard heraus irgendwie für alle Rechnungen gleichzeitig (die offen sind)? Das wäre super. Ich komm einfach nicht drauf :(

      • Linda_Dauble
      • vor 10 Monaten
      • Gemeldet - anzeigen

      Ich hab die Datenbank einmal kopiert und um alles unnötige bereinigt (sowie um Geschäftsgeheimnisse, natürlich). Die Fehlermeldungen am Anfang sind, weil ich zig Tabellen gelöscht hab, lass dich davon bitte nicht stören.

      Ich hatte zwischendurch überall versucht, mit den Buttons zu arbeiten, ich würde es am Liebsten aber aufm Dashboard mit dem (nach CSV Import) Button haben. Bestimmt übersehe ich was total einfaches.

    • Ninox-Professional
    • planoxpro
    • vor 10 Monaten
    • Gemeldet - anzeigen
     said:
    Wenn ich den Betrag in das if mit einbaue, gibt es natürlich Probleme, wenn jemand den falschen Betrag überweist (brutto statt netto, Centbeträge, usw).

    Oder Skonto ... ;)

    Ja, als eindeutiges Suchkriterium taugt der Zahlbetrag natürlich nicht. Auswerten muss man ihn aber eigentlich immer, denn es sollen ja keine Rechnungen als "Bezahlt" markiert werden, wenn erst 50 % beglichen  wurden. Als weitere Besonderheit kann es jau auch sein, dass mehrere Rechnungen mit einer Überweisung bezahlt wurden. Aber das sind dann die individuellen Feinheiten.

    • Ninox-Professional
    • planoxpro
    • vor 10 Monaten
    • Gemeldet - anzeigen
     said:
    Ich wollte das im Dashboard ausführen,

    Ah, okay. Und das Dashboard ist 1:N mit den Bankingdaten verknüpft?

    Egal, es geht natürlich auch vom Dashboard aus, um gleich alle Rechnungen durchzugehen. Man braucht dann halt zwei Schleifen. Hier ein Beispiel (wiederum ohne Berücksichtigung irgendwelcher Verknüpfungen):

    let myOP := (select Rechnungen where Status != 2);
    for x in myOP do
       let myRE := x.'Rechnungsnr.';
       for y in select Bankingdaten do
          if contains(y.Verwendungszweck, myRE) then
             x.(Status := 2)
          end
       end
    end

     

    Letztlich hängt wie gesagt natürlich noch mehr dran. Aber als Basis müsste der Code eigentlich funktionieren.

    Edit: Sehe gerade, dass du die DB angehängt hast. Habe jetzt aber noch nicht reingeschaut. Probiere doch erst mal den Code oben.

      • Linda_Dauble
      • vor 10 Monaten
      • Gemeldet - anzeigen

      Du bist einfach der klügste Mensch der Welt für mich gerade. Ich dachte mir schon, dass ich das dann irgendwie über zwei Schleifen machen muss, ich bin nur einfach nicht drauf gekommen, wie.

      Es klappt wunderbar, danke danke danke!!!!

Content aside

  • Status Answered
  • vor 10 MonatenZuletzt aktiv
  • 13Antworten
  • 60Ansichten
  • 3 Folge bereits