Exemple #1
0
        public static List <SwiftStatement> Serialize(string STA, string Account, bool writeToFile = false, bool pending = false)
        {
            int LineCounter = 0;

            string swiftTag  = "";
            string swiftData = "";

            SWIFTStatements = new List <SwiftStatement>();
            SWIFTStatement  = null;

            if (STA == null || STA.Length == 0)
            {
                return(SWIFTStatements);
            }

            string dir = null;

            if (writeToFile)
            {
                dir = FinTsConfig.ProgramBaseDir;

                dir = Path.Combine(dir, "STA");

                string filename = Path.Combine(dir, Helper.MakeFilenameValid(Account + "_" + DateTime.Now + ".STA"));

                if (!Directory.Exists(dir))
                {
                    Directory.CreateDirectory(dir);
                }

                // STA
                if (!File.Exists(filename))
                {
                    using (File.Create(filename))
                    { };

                    File.AppendAllText(filename, STA);
                }
                else
                {
                    File.AppendAllText(filename, STA);
                }
            }

            while (STA.Length > 0)
            {
                string line = Read(ref STA);

                LineCounter++;

                if (line.Trim() == "-") // end of block
                {
                    // Process previously read swift chunk
                    if (swiftTag.Length > 0)
                    {
                        Data(swiftTag, swiftData);
                    }

                    swiftTag  = string.Empty;
                    swiftData = string.Empty;
                    continue;
                }

                if (line.Length > 0)
                {
                    // A swift chunk starts with a swiftTag, which is between colons
                    if (Regex.IsMatch(line, @"^:[\w]+:"))
                    {
                        // Process previously read swift chunk
                        if (swiftTag.Length > 0)
                        {
                            Data(swiftTag, swiftData);
                        }

                        int posColon = line.IndexOf(":", 2);

                        swiftTag  = line.Substring(1, posColon - 1);
                        swiftData = line.Substring(posColon + 1);
                    }
                    else
                    {
                        // The swift chunk is spread over several lines
                        swiftData = swiftData + "\r\n" + line;
                    }
                }
            }

            if (swiftTag.Length > 0)
            {
                Data(swiftTag, swiftData);
            }

            // If there are remaining unprocessed statements - add them
            if (SWIFTStatement != null)
            {
                SWIFTStatements.Add(SWIFTStatement);

                // Process missing input dates
                foreach (var tx in SWIFTStatement.SwiftTransactions)
                {
                    if (tx.InputDate == default)
                    {
                        tx.InputDate = SWIFTStatement.EndDate;
                    }
                }

                SWIFTStatement = null;
            }

            // Set pending
            if (pending)
            {
                foreach (var stmt in SWIFTStatements)
                {
                    stmt.Pending = true;
                }
            }

            // Parse SEPA purposes
            foreach (var stmt in SWIFTStatements)
            {
                foreach (var tx in stmt.SwiftTransactions)
                {
                    if (string.IsNullOrWhiteSpace(tx.Description))
                    {
                        continue;
                    }

                    // Collect all occuring SEPA purposes ordered by their position
                    List <Tuple <int, SepaPurpose> > indices = new List <Tuple <int, SepaPurpose> >();
                    foreach (SepaPurpose sepaPurpose in Enum.GetValues(typeof(SepaPurpose)))
                    {
                        string prefix = $"{sepaPurpose}+";
                        var    idx    = tx.Description.IndexOf(prefix);
                        if (idx >= 0)
                        {
                            indices.Add(Tuple.Create(idx, sepaPurpose));
                        }
                    }
                    indices = indices.OrderBy(v => v.Item1).ToList();

                    // Then get the values
                    for (int i = 0; i < indices.Count; i++)
                    {
                        var beginIdx = indices[i].Item1 + $"{indices[i].Item2}+".Length;
                        var endIdx   = i < indices.Count - 1 ? indices[i + 1].Item1 : tx.Description.Length;

                        var value = tx.Description.Substring(beginIdx, endIdx - beginIdx);
                        tx.SepaPurposes[indices[i].Item2] = value;
                    }
                }
            }

            if (Trace.Enabled)
            {
                foreach (SwiftStatement statement in SWIFTStatements)
                {
                    var ID           = statement.Id;
                    var AccountCode  = statement.AccountCode;
                    var BanksortCode = statement.BankCode;
                    var Currency     = statement.Currency;
                    var StartDate    = $"{statement.StartDate:d}";
                    var StartBalance = statement.StartBalance.ToString();
                    var EndDate      = $"{statement.EndDate:d}";
                    var EndBalance   = statement.EndBalance.ToString();

                    foreach (SwiftTransaction transaction in statement.SwiftTransactions)
                    {
                        var PartnerName  = transaction.PartnerName;
                        var AccountCode_ = transaction.AccountCode;
                        var BankCode     = transaction.BankCode;
                        var Description  = transaction.Description;
                        var Text         = transaction.Text;
                        var TypeCode     = transaction.TypeCode;
                        var Amount       = transaction.Amount.ToString();

                        var UMS = "++STARTUMS++" + "ID: " + ID + " ' " +
                                  "AccountCode: " + AccountCode + " ' " +
                                  "BanksortCode: " + BanksortCode + " ' " +
                                  "Currency: " + Currency + " ' " +
                                  "StartDate: " + StartDate + " ' " +
                                  "StartBalance: " + StartBalance + " ' " +
                                  "EndDate: " + EndDate + " ' " +
                                  "EndBalance: " + EndBalance + " ' " +
                                  "PartnerName: " + PartnerName + " ' " +
                                  "BankCode: " + BankCode + " ' " +
                                  "Description: " + Description + " ' " +
                                  "Text: " + Text + " ' " +
                                  "TypeCode: " + TypeCode + " ' " +
                                  "Amount: " + Amount + " ' " + "++ENDUMS++";

                        dir = FinTsConfig.ProgramBaseDir;
                        dir = Path.Combine(dir, "MT940");

                        string filename_ = Path.Combine(dir, Helper.MakeFilenameValid(Account + "_" + DateTime.Now + ".MT940"));

                        if (!Directory.Exists(dir))
                        {
                            Directory.CreateDirectory(dir);
                        }

                        // MT940
                        if (!File.Exists(filename_))
                        {
                            using (File.Create(filename_))
                            { };

                            File.AppendAllText(filename_, UMS);
                        }
                        else
                        {
                            File.AppendAllText(filename_, UMS);
                        }
                    }
                }
            }

            return(SWIFTStatements);
        }
Exemple #2
0
        private static void Data(string swiftTag, string swiftData)
        {
            if (SWIFTStatement != null)
            {
                SWIFTStatement.Lines.Add(new SwiftLine(swiftTag, swiftData));
            }

            if (swiftTag == "OS")
            {
                // Ignore
            }
            else if (swiftTag == "20")
            {
                // 20 is used for each "page" of the SWIFTStatement; but we want to put all SWIFTTransactions together
                // the whole SWIFTStatement closes with 62F
                if (SWIFTStatement == null)
                {
                    SWIFTStatement = new SwiftStatement()
                    {
                        Type = swiftData
                    };
                    SWIFTStatement.Lines.Add(new SwiftLine(swiftTag, swiftData));
                }
            }
            else if (swiftTag == "25")
            {
                int posSlash = swiftData.IndexOf("/");
                if (posSlash >= 0)
                {
                    SWIFTStatement.BankCode = swiftData.Substring(0, posSlash);
                    if (posSlash < swiftData.Length)
                    {
                        SWIFTStatement.AccountCode = LTrim(swiftData.Substring(posSlash + 1));
                    }
                }
            }
            else if (swiftTag.StartsWith("60")) // Anfangssaldo
            {
                // 60M is the start balance on each page of the SWIFTStatement.
                // 60F is the start balance of the whole SWIFTStatement.

                // First character is D or C
                int DebitCreditIndicator = (swiftData[0] == 'D' ? -1 : +1);

                // Next 6 characters: YYMMDD
                swiftData = swiftData.Substring(1);

                // Start date YYMMDD
                DateTime postingDate = new DateTime(2000 + Convert.ToInt32(swiftData.Substring(0, 2)),
                                                    Convert.ToInt32(swiftData.Substring(2, 2)),
                                                    Convert.ToInt32(swiftData.Substring(4, 2)));

                // Next 3 characters: Currency
                // Last characters: Balance with comma for decimal point
                SWIFTStatement.Currency = swiftData.Substring(6, 3);
                try
                {
                    decimal balance = DebitCreditIndicator * Convert.ToDecimal(swiftData.Substring(9).Replace(",",
                                                                                                              Thread.CurrentThread.CurrentCulture.NumberFormat.CurrencyDecimalSeparator));

                    // Use first start balance. If missing, use intermediate balance.
                    if (swiftTag == "60F" || SWIFTStatement.StartBalance == 0 && swiftTag == "60M")
                    {
                        SWIFTStatement.StartBalance = balance;
                        SWIFTStatement.EndBalance   = balance;
                    }
                }
                catch (FormatException)
                {
                    Log.Write($"Invalid balance: {swiftData}");
                }

                if (swiftTag == "60F" || swiftTag == "60M")
                {
                    SWIFTStatement.StartDate = postingDate;
                }
            }
            else if (swiftTag == "28C")
            {
                // this contains the number of the SWIFTStatement and the number of the page
                // only use for first page
                if (SWIFTStatement.SwiftTransactions.Count == 0)
                {
                    if (swiftData.IndexOf("/") != -1)
                    {
                        SWIFTStatement.Id = swiftData.Substring(0, swiftData.IndexOf("/"));
                    }
                    else
                    {
                        // Realtime SWIFTStatement.
                        // Not use SWIFTStatement number 0, because Sparkasse has 0/1 for valid SWIFTStatements
                        SWIFTStatement.Id = string.Empty;
                    }
                }
            }
            else if (swiftTag == "61")
            {
                // If there is no SWIFTStatement available, create one
                if (SWIFTStatement == null)
                {
                    SWIFTStatement = new SwiftStatement();
                    SWIFTStatement.Lines.Add(new SwiftLine(swiftTag, swiftData));
                }

                SwiftTransaction SWIFTTransaction = new SwiftTransaction();
                SWIFTStatement.SwiftTransactions.Add(SWIFTTransaction);

                // Valuta date (YYMMDD)
                try
                {
                    SWIFTTransaction.ValueDate = new DateTime(2000 + Convert.ToInt32(swiftData.Substring(0, 2)),
                                                              Convert.ToInt32(swiftData.Substring(2, 2)),
                                                              Convert.ToInt32(swiftData.Substring(4, 2)));
                }
                catch (ArgumentOutOfRangeException)
                {
                    // we have had the situation in the bank file with a date 30 Feb 2010.
                    // probably because the instruction by the donor is to transfer the money on the 30 day each month
                    // use the last day of the month
                    int year  = 2000 + Convert.ToInt32(swiftData.Substring(0, 2));
                    int month = Convert.ToInt32(swiftData.Substring(2, 2));
                    int day   = DateTime.DaysInMonth(year, month);

                    SWIFTTransaction.ValueDate = new DateTime(year, month, day);
                }

                swiftData = swiftData.Substring(6);

                // Optional: Posting date (MMDD)
                if (Regex.IsMatch(swiftData, @"^\d{4}"))
                {
                    int year  = SWIFTTransaction.ValueDate.Year;
                    int month = Convert.ToInt32(swiftData.Substring(0, 2));
                    int day   = Convert.ToInt32(swiftData.Substring(2, 2));

                    // Posting date 30 Dec 2020, Valuta date 1 Jan 2020
                    if (month > SWIFTTransaction.ValueDate.Month && month == SWIFTTransaction.ValueDate.AddMonths(-1).Month)
                    {
                        year--;
                    }
                    // Posting date 1 Jan 2020, Valuta date 30 Dec 2020
                    else if (month < SWIFTTransaction.ValueDate.Month && month == SWIFTTransaction.ValueDate.AddMonths(1).Month)
                    {
                        year++;
                    }

                    SWIFTTransaction.InputDate = new DateTime(year, month, day);

                    swiftData = swiftData.Length > 4 ? swiftData.Substring(4) : string.Empty;
                }

                // Amount - some characters followed by an 'N'
                if (Regex.IsMatch(swiftData, @"^.+N"))
                {
                    // Debit or credit, or storno debit or credit
                    int debitCreditIndicator = 0;
                    if (swiftData[0] == 'R')
                    {
                        // Storno means: reverse the debit credit flag
                        debitCreditIndicator = (swiftData[1] == 'D' ? 1 : -1);
                        swiftData            = swiftData.Substring(2);
                    }
                    else
                    {
                        debitCreditIndicator = (swiftData[0] == 'D' ? -1 : 1);
                        swiftData            = swiftData.Substring(1);
                    }

                    // Sometimes there is something about currency
                    if (char.IsLetter(swiftData[0]))
                    {
                        // Just skip it for the moment
                        swiftData = swiftData.Substring(1);
                    }

                    // The amount, finishing with N
                    SWIFTTransaction.Amount =
                        debitCreditIndicator * Convert.ToDecimal(swiftData.Substring(0, swiftData.IndexOf("N")).Replace(",",
                                                                                                                        Thread.CurrentThread.CurrentCulture.NumberFormat.CurrencyDecimalSeparator));

                    SWIFTStatement.EndBalance += SWIFTTransaction.Amount;

                    var constIdx = swiftData.IndexOf("N");
                    swiftData = swiftData.Length > constIdx?swiftData.Substring(constIdx) : string.Empty;
                }
                else
                {
                    return;
                }

                // Buchungsschlüssel
                if (Regex.IsMatch(swiftData, @"^N[A-Z0-9]{3}"))
                {
                    SWIFTTransaction.TransactionTypeId = swiftData.Substring(0, 4);

                    swiftData = swiftData.Length > 4 ? swiftData.Substring(4) : string.Empty;
                }
                else
                {
                    return;
                }

                // customer reference
                if (Regex.IsMatch(swiftData, @"^.+"))
                {
                    int idxDelimiter = swiftData.IndexOf("//");
                    if (idxDelimiter > 0)
                    {
                        SWIFTTransaction.CustomerReference = swiftData.Substring(0, idxDelimiter);
                    }
                    else
                    {
                        SWIFTTransaction.CustomerReference = swiftData;
                    }

                    if (idxDelimiter > 0)
                    {
                        swiftData = swiftData.Length > idxDelimiter + 2 ? swiftData.Substring(idxDelimiter + 2) : string.Empty;
                    }
                    else
                    {
                        swiftData = string.Empty;
                    }
                }
                else
                {
                    return;
                }

                // Optional: bank reference; ends with CR/LF if followed by other data
                if (Regex.IsMatch(swiftData, @"^.+?\r\n", RegexOptions.Singleline))
                {
                    int lineBreakIdx = swiftData.IndexOf("\r\n");
                    if (lineBreakIdx > 0)
                    {
                        SWIFTTransaction.BankReference = swiftData.Substring(0, lineBreakIdx);
                        swiftData = swiftData.Substring(lineBreakIdx + 2);
                    }
                    else
                    {
                        SWIFTTransaction.BankReference = swiftData;
                        swiftData = string.Empty;
                    }
                }

                // Optional: other data
                if (!string.IsNullOrWhiteSpace(swiftData))
                {
                    SWIFTTransaction.OtherInformation = swiftData;
                }
            }
            else if (swiftTag == "86")
            {
                // Remove line breaks
                swiftData = swiftData.Replace("\r\n", string.Empty);

                SwiftTransaction SWIFTTransaction = SWIFTStatement.SwiftTransactions[SWIFTStatement.SwiftTransactions.Count - 1];

                // Geschaeftsvorfallcode
                SWIFTTransaction.TypeCode = swiftData.Substring(0, 3);

                swiftData = swiftData.Substring(3);

                if (swiftData.Length == 0)
                {
                    return;
                }

                char separator = swiftData[0];

                swiftData = swiftData.Substring(1);

                string[] elements = swiftData.Split(new char[] { separator });
                string   lastDescriptionSubfield = string.Empty;
                foreach (string element in elements)
                {
                    int    key   = 0;
                    string value = element;

                    try
                    {
                        key   = Convert.ToInt32(element.Substring(0, 2));
                        value = element.Substring(2);
                    }
                    catch
                    {
                        // If there is a question mark in the description, then we get here
                    }

                    if (key == 0)
                    {
                        // Buchungstext
                        SWIFTTransaction.Text = value;
                    }
                    else if (key == 10)
                    {
                        // Primanotennummer
                        SWIFTTransaction.Primanota = value;
                    }
                    else if ((key >= 11) && (key <= 19))
                    {
                        // Ignore
                        // Unknown meaning
                    }
                    else if ((key >= 20) && (key <= 29))
                    {
                        // No space between description lines
                        if (value.EndsWith(" "))
                        {
                            SWIFTTransaction.Description += value;
                        }
                        else
                        {
                            SWIFTTransaction.Description += value + " ";
                        }
                        AssignDescriptionSubField(SWIFTTransaction, value, ref lastDescriptionSubfield);
                    }
                    else if (key == 30)
                    {
                        SWIFTTransaction.BankCode = value;
                    }
                    else if (key == 31)
                    {
                        SWIFTTransaction.AccountCode = value;
                    }
                    else if ((key == 32) || (key == 33))
                    {
                        SWIFTTransaction.PartnerName += value;
                    }
                    else if (key == 34)
                    {
                        // Textschlüsselergänzung
                        SWIFTTransaction.TextKeyAddition = value;
                    }
                    else if ((key == 35) || (key == 36))
                    {
                        // Empfängername
                        SWIFTTransaction.Description += value;
                    }
                    else if ((key >= 60) && (key <= 63))
                    {
                        SWIFTTransaction.Description += value;
                        AssignDescriptionSubField(SWIFTTransaction, value, ref lastDescriptionSubfield);
                    }
                    else
                    {
                        // Unknown key
                        return;
                    }
                }
            }
            else if (swiftTag.StartsWith("62")) // Schlusssaldo
            {
                // 62M: Finish page
                // 62F: Finish SWIFTStatement
                int debitCreditIndicator = (swiftData[0] == 'D' ? -1 : 1);
                swiftData = swiftData.Substring(1);

                // Posting date YYMMDD
                DateTime postingDate = new DateTime(2000 + Convert.ToInt32(swiftData.Substring(0, 2)),
                                                    Convert.ToInt32(swiftData.Substring(2, 2)),
                                                    Convert.ToInt32(swiftData.Substring(4, 2)));

                swiftData = swiftData.Substring(6);

                // Currency
                if (swiftData.Length > 3) // Assure that currency and end balance are valid
                {
                    swiftData = swiftData.Substring(3);

                    // Sometimes, this line is the last line, and it has -NULNULNUL at the end
                    if (swiftData.Contains("-\0"))
                    {
                        swiftData = swiftData.Substring(0, swiftData.IndexOf("-\0"));
                    }

                    // End balance
                    decimal endBalance = debitCreditIndicator * Convert.ToDecimal(swiftData.Replace(",",
                                                                                                    Thread.CurrentThread.CurrentCulture.NumberFormat.CurrencyDecimalSeparator));
                    SWIFTStatement.EndBalance = endBalance;
                }

                if (swiftTag == "62F" || swiftTag == "62M")
                {
                    SWIFTStatement.EndDate = postingDate;
                    SWIFTStatements.Add(SWIFTStatement);

                    // Process missing input dates
                    foreach (var tx in SWIFTStatement.SwiftTransactions)
                    {
                        if (tx.InputDate == default)
                        {
                            tx.InputDate = SWIFTStatement.EndDate;
                        }
                    }

                    SWIFTStatement = null;
                }
            }
            else if (swiftTag == "64")
            {
                // Valutensaldo
            }
            else if (swiftTag == "65")
            {
                // Future valutensaldo
            }

            // Begin MT942
            else if (swiftTag == "34F")
            {
                if (swiftData.Length >= 3)
                {
                    SWIFTStatement.Currency = swiftData.Substring(0, 3);
                    swiftData = swiftData.Length > 3 ? swiftData.Substring(3) : string.Empty;
                }

                // Kleinster Betrag der gemeldeten Umsätze
                if (Regex.IsMatch(swiftData, @"D?\d+,\d*"))
                {
                    bool    debit  = swiftData.Substring(0, 1) == "D";
                    decimal amount = 0;
                    if (debit)
                    {
                        decimal.TryParse(swiftData.Substring(1), out amount);
                        amount = amount * -1;
                    }
                    else
                    {
                        decimal.TryParse(swiftData, out amount);
                    }

                    SWIFTStatement.SmallestAmount = amount;
                }
                // Kleinster Betrag der gemeldeten Haben-Umsätze
                else if (Regex.IsMatch(swiftData, @"C\d+,\d*"))
                {
                    decimal.TryParse(swiftData.Substring(1), out decimal amount);

                    SWIFTStatement.SmallestCreditAmount = amount;
                }
            }
            else if (swiftTag == "13") // Deutsche Bank
            {
                if (Regex.IsMatch(swiftData, @"\d{10}"))
                {
                    DateTime.TryParseExact(swiftData, "yyMMddHHmm", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime creationDate);

                    SWIFTStatement.CreationDate = creationDate;
                }
            }
            else if (swiftTag == "13D")
            {
                if (Regex.IsMatch(swiftData, @"\d{10}(\+|-)\d{4}"))
                {
                    // Easier parsing
                    // 1912090901+0100 -> 1912090901+01:00
                    var dateStr = swiftData.Substring(0, 13) + ":" + swiftData.Substring(13, 2);
                    DateTimeOffset.TryParseExact(dateStr, "yyMMddHHmmzzz", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTimeOffset dateTimeOffset);

                    SWIFTStatement.CreationDate = dateTimeOffset.DateTime;
                }
                else
                {
                    DateTime.TryParseExact(swiftData, "yyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime creationDate);

                    SWIFTStatement.CreationDate = creationDate;
                }
            }
            else if (swiftTag == "90D" || swiftTag == "90C")
            {
                bool debit          = swiftTag == "90D";
                bool previousTag90d = !debit && SWIFTStatement == null; // Previous tag has been 90D

                if (previousTag90d)
                {
                    SWIFTStatement = SWIFTStatements.LastOrDefault();
                }

                if (SWIFTStatement == null)
                {
                    return;
                }

                int     count    = 0;
                decimal amount   = 0;
                string  currency = null;
                var     match    = Regex.Match(swiftData, @"(\d+)([A-Z]{3})(\d+(,\d+)?)");
                if (match.Success)
                {
                    int.TryParse(match.Groups[1].Value, out count);

                    currency = match.Groups[2].Value;

                    decimal.TryParse(match.Groups[3].Value, NumberStyles.Number | NumberStyles.AllowDecimalPoint, CultureInfo.GetCultureInfo("de-DE"), out amount);
                }

                if (SWIFTStatement.Currency == null)
                {
                    SWIFTStatement.Currency = currency;
                }

                if (debit)
                {
                    SWIFTStatement.CountDebit  = count;
                    SWIFTStatement.AmountDebit = amount * -1;
                }
                else
                {
                    SWIFTStatement.CountCredit  = count;
                    SWIFTStatement.AmountCredit = amount;
                }

                if (debit)
                {
                    SWIFTStatements.Add(SWIFTStatement);
                    SWIFTStatement = null;
                }
                else
                {
                    if (!previousTag90d)
                    {
                        SWIFTStatements.Add(SWIFTStatement);
                    }
                    SWIFTStatement = null;
                }
            }
            // End MT942

            else
            {
                // Unknown tag
                return;
            }
        }