Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   MT940 importieren (https://www.delphipraxis.net/188226-mt940-importieren.html)

mm1256 11. Feb 2016 15:23

MT940 importieren
 
Hallo,

vorab, Tante Google hab ich schon befragt, und leider nichts gefunden ausser einer alten unbeantworteten Foren-Frage aus 2004 :roll:

Ich möchte meinen Kontoauszug-Import um das Format MT940 (Dateierweiterung .sta) erweitern. Da es einfache Textdateien sind, ist das auch keine große technische Herausforderung. Wenn nur die liebe Zeit nicht wäre. Darum die Frage: Gibt es hierzu etwas?

mm1256 12. Feb 2016 09:23

AW: MT940 importieren
 
Die Frage hat sich erledigt, die Importfunktion ist schon fast fertig

kaju74 1. Jun 2016 08:34

AW: MT940 importieren
 
Moin.

Da ich eben vor demselben Problem stehe, hättest Du da evtl. was für die Gemeinschaft? ;-))

LG,
kaju

rokli 1. Jun 2016 10:59

AW: MT940 importieren
 
Hallo Otto,

wäre schon sehr nett, wenn Du uns teilhaben lässt ... :thumb:

da ich auch eine Anfrage liegen haben, ob wir das Format importieren können!

Gruß
Rolf

kaju74 1. Jun 2016 11:34

AW: MT940 importieren
 
...nur wenn's okay für Dich ist...man muss ja nicht alles doppelt machen ;-)

LG,
kaju

kaju74 1. Jun 2016 13:51

AW: MT940 importieren
 
Ich habe derweil mal angefangen. Benötige das zum Import von Zahlungsvorgängen (ähnlich wie beim Online-Banking). Habe für mich mal die relevanten Informationen aus den existierenden Format-Beschreibungen extrahiert. Vielleicht hilft's ja ;-)

Das MT940 Format beschreibt den Satzaufbau zur Übermittlung von Kontoauszugsdaten (SEPA).

Die Datei liegt üblicherweise im ASCII-Format vor und besteht aus reinem Text. Die "Datensätze" werden durch einleitende Steuerungszeichen identifiziert. Der Aufbau der Datei muss nicht zwangsweise Zeilen-basiert sein. Es könnten Dateien existieren, deren Inhalt auf eine oder wenige Zeilen komprimiert ist. Ein Parser muss in der Lage sein, dieses Format so lesen zu können.

Nachfolgend eine Beschreibung der wichtigsten Daten:

Zitat:

:20:[16-Auftraggeber Kennzeichen]

:25:[11-BIC]/[24-IBAN]
Bankleitzahl (BIC) / Kontonr. (IBAN)

:28C:[5-NR]/[5-BLATT]
Auszugsnummer / Blattnummer (optional)

:60F:[C|D][JJMMTT]EUR[Betrag]
Anfangssaldo:
C=Haben|D=Soll, Datum, Betrag (xx,yy)

:61:[JJMMTT][MMTT][C|D|RC|RD][Betrag]N[3-BU][16-REF]
Umsatzdetails #1:Valutadatum,Buchungsdatum(optional), Soll|Haben|Storno Soll|Storno Haben, Betrag (xx,yy), Buchungsschlüssel, Kundenreferenz

:86:[3-CODE]?00[27-Buchungstext]
?20-?29[27-Verwendungszweck]
?60-?63[27-Verwendungszweck]
?30[12-Auftraggeber-BIC]
?31[24-Auftraggeber-IBAN]
?32-?33 [27-Auftraggeber Name]
?34 [27-Rücklastschriften]
Umsatzdetails #2:Geschäftsvorfall-Code (3-stellig, bei SEPA als 1xx)
@All: Wie zum Geier nutzt man hier die Table-Tags?

Gruß,
kaju

mm1256 1. Jun 2016 14:33

AW: MT940 importieren
 
Liste der Anhänge anzeigen (Anzahl: 2)
Hallo Kollegen,

klar hab ich was für euch. Die Frage ist, ob ihr viel damit anfangen könnt. Hinzu kommt, dass ich heute (und voraussichtlich den Rest der Woche) so viel um die Ohren habe, dass ich euch leider kein fertiges Beispiel geben kann.

Die RuMT940.pas enthält die Deklarationen des MT940-Formats und die MT940Import.pas ist ein Auszug aus der Unit, welche meine eigentliche Importroutine enthält. Diese Unit ist wie gesagt nicht compilierfähig, weil es nur ein Auszug ist. Die komplette Unit würde euch nichts nützen, weil ich die MT940-Daten natürlich in die Datenbank schreibe, und da hat wohl jeder seinen eigenen Aufbau.

Hinzu kommt: Da ich Auszüge in mehreren Formaten importiere (dBase, CSV, MT940...) und das dazu noch von verschiedenen Banking-Programmen habe ich das Problem erkennen zu müssen, ob ein Auszug bereits einmal importiert wurde. Darum schreibe ich die wichtigsten (zu importierenden) Daten erst mal in den Record "TBBXHashDat". Dann bilde ich aus den Daten im Record einen String und bilde daraus einen MD5-Hash. Dieser MD5 wird in der Datenbank als String-Feld mit gespeichert und ist indiziert, wodurch ich beim Import mit einem einzigen Index sehr schnell feststellen kann, ob ein Auszug bereits importiert worden ist.

Das funktioniert auch, wenn der User den Datenexport in seinem Banking-Programm in verschiedenen Formaten durchgeführt hat. Macht zwar keinen Sinn, aber, ich möchte mir meine Kontoauszug-Datenbank nicht mit Müll zuschießen. Schließlich wird daraus die Fibu bebucht.

Wenn ihr noch Fragen habt, jederzeit gerne, aber bitte berücksichtigen, momentan brennt meine Hütte und darum kann es etwas dauern. Mir ist auch klar, dass man den Import bzw. die Importroutine etwas eleganter gestalten und effektiver schreiben könnte. Wenn ich mal mehr Zeit dazu habe, dann passiert das auch 8-)

sh17 1. Jun 2016 14:37

AW: MT940 importieren
 
Interesse an einen GitHub Repo? https://github.com/DelphiPraxis

kaju74 2. Jun 2016 10:19

AW: MT940 importieren
 
Guten Morgen.

Ersteinmal vielen Dank für Deinen Code Auszug. Gucke ich mir gleich mal an ;-) Würde heute sonst anfangen, das selber zu implementieren, aber damit ist es schonmal eine große Hilfe.

LG,
kaju

kaju74 2. Jun 2016 18:26

AW: MT940 importieren
 
Mit Hilfe der Code-Snippets konnte ich heute einen eigenen Parser schreiben, der das Ganze schon ganz gut für mich erledigt. Ein paar Kleinigkeiten fehlen noch, aber vom Prinzip her funktioniert. Auch hier ist wieder das Problem, das ich meine eigenen Methoden nutze, vom Prinzip her dürfte die Logik aber klar sein...falls es einem hilfe ;-)

Delphi-Quellcode:
function TDataModShared.ParseContent(Parser: TNXStringParser): Boolean;
var
  Str: string;
  Item: TPaymentItem;
  Detail: TNXString;
  Process: Boolean;
  FormatSettings: TFormatSettings;
begin
  { set result }
  Result := True;

  { init vars }
  Process := False;

  { set local format settings }
  GetLocaleFormatSettings(GetSystemDefaultLangID, FormatSettings);
  FormatSettings.DateSeparator := '/';
  FormatSettings.ShortDateFormat := 'yyyy/mm/dd';
  FormatSettings.DecimalSeparator := ',';

  { endless loop... }
  while True do
  begin
    { skip to next token }
    Parser.SkipToken;

    { end of file? }
    if Parser.Token = toEOF then
      Break;

    { not an main identifier? }
    if Parser.Token <> ':' then
      Continue;

    { process special parts }
    case TNXStrings.IndexOf(Parser.SkipToToken(':'), ['61', '86']) of
      0: // sales part #61
        try
          { reset process flag }
          Process := False;

          { get parsed token string }
          Str := Parser.SkipTokenString;

          { check for debit payments only! }
          if not(SameText('D', TNXStrings.Mid(Str, 11, 1))) then
          begin
            { skip to end-block, but freeze token pos }
            Parser.SkipToTokenString(':6', True);

            { continue }
            Continue;
          end;

          { reset payment item }
          FillChar(Item, SizeOf(TPaymentItem), #0);

          { update entry date }
          Item.EntryDate := StrToDateDef(
            TNXStrings.Inject('20' + TNXStrings.Left(Str, 6), [4, 6], '/'), Date, FormatSettings
          );

          { update validity date }
          Item.ValidityDate := TNXStrings.FormatDate(Item.EntryDate);

          { update amount }
          Str := TNXStrings.RestOf(Str, 12);
          Item.Amount := StrToFloatDef(TNXStrings.Before('N', Str), 0.00, FormatSettings);

          { set process flag }
          Process := True;
        except
          { catch all index errors }
        end;

      1: // sales part #86
        if Process then
        begin
          { reset process flag }
          Process := False;

          { extract whole content up to next block }
          Str := NXString.From(Parser.SkipToTokenString(':6', True)).EnsureNoSuffix(':').Text;

          { only process sepa-compatible payments }
          if not(TNXStrings.HasPrefix('1', Str)) then
            Continue;

          { create single line with all additional details }
          Str := NXString.From(Str).After('?').EnsurePrefix('?').Strip([#13, #10]).Replace(#32, '_').Text;

          { iterate through all details seperated by ?-ids }
          for Detail in NXStrings.From(Str, '?').StripBlanks do
          begin
            { create simple id#content lines }
            Detail.Inject([2], '#');

            { get detail content }
            Str := NXString.From(Detail.Text).After('#').Replace('_', #32).Text;

            { get detail by id }
            case StrToIntDef(TNXStrings.Before('#', Detail.Text), -1) of
              { purposes }
              20..29, 60..63:
                Item.Purposes := TNXStrings.Stake(Item.Purposes, Str, '');

              { sender info }
              30: Item.Sender.BIC := Str;
              31: Item.Sender.IBAN := Str;
              32, 33: Item.Sender.Identifier := Str;
            end;
          end;

          { add item to list }
          FPlugIn.AddItem(Item);
        end;
    end;
  end;
end;
LG,
kaju


Alle Zeitangaben in WEZ +1. Es ist jetzt 11:25 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz