Beispiel #1
0
        /// <summary>
        /// processing MT940 file
        /// </summary>
        public void ProcessFileContent(string AContent)
        {
            Int32 LineCounter = 0;

            try
            {
                string swiftTag = "";
                string swiftData = "";
                statements = new List <TStatement>();
                currentStatement = null;

                while (AContent.Length > 0)
                {
                    string line = ReadLine(ref AContent);
                    LineCounter++;

                    if ((line.Length > 0) && (!line.StartsWith("-")))
                    {
                        // a swift chunk starts with a swiftTag, which is between colons
                        if (line.StartsWith(":"))
                        {
                            // process previously read swift chunk
                            if (swiftTag.Length > 0)
                            {
                                // Console.WriteLine(" Tag " + swiftTag + " Data " + swiftData);
                                HandleSwiftData(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 + line;
                        }
                    }
                }

                if (swiftTag.Length > 0)
                {
                    HandleSwiftData(swiftTag, swiftData);
                }
            }
            catch (Exception e)
            {
                throw new Exception(
                    "problem with MT940 file; line: " + LineCounter.ToString() + "; " + e.Message + Environment.NewLine + e.StackTrace);
            }
        }
Beispiel #2
0
        /// <summary>
        /// processing CAMT file
        /// </summary>
        public void ProcessFileContent(string content, bool AParsePreviousYear, out TVerificationResultCollection AVerificationResult)
        {
            statements          = new List <TStatement>();
            AVerificationResult = new TVerificationResultCollection();

            CultureInfo backupCulture = Thread.CurrentThread.CurrentCulture;

            try
            {
                XmlDocument doc = new XmlDocument();
                doc.LoadXml(content);
                XmlNode nodeDocument = doc.DocumentElement;

                string CAMTVersion        = "CAMT.53";
                XmlNamespaceManager nsmgr = GetNamespaceManager(doc, "urn:iso:std:iso:20022:tech:xsd:camt.053.001.02");

                if (nsmgr == null)
                {
                    CAMTVersion = "CAMT.52";
                    nsmgr       = GetNamespaceManager(doc, "urn:iso:std:iso:20022:tech:xsd:camt.052.001.02");
                }

                if (nsmgr == null)
                {
                    throw new Exception("expecting xmlns for CAMT.52 or CAMT.53");
                }

                XmlNodeList stmts = null;

                if (CAMTVersion == "CAMT.53")
                {
                    stmts = nodeDocument.SelectNodes("camt:BkToCstmrStmt/camt:Stmt", nsmgr);
                }
                else if (CAMTVersion == "CAMT.52")
                {
                    stmts = nodeDocument.SelectNodes("camt:BkToCstmrAcctRpt/camt:Rpt", nsmgr);
                }

                Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;

                foreach (XmlNode nodeStatement in stmts)
                {
                    TStatement stmt = new TStatement();

                    stmt.id          = nodeStatement.SelectSingleNode("camt:ElctrncSeqNb", nsmgr).InnerText;
                    stmt.accountCode = nodeStatement.SelectSingleNode("camt:Acct/camt:Id/camt:IBAN", nsmgr).InnerText;
                    stmt.bankCode    = nodeStatement.SelectSingleNode("camt:Acct/camt:Svcr/camt:FinInstnId/camt:BIC", nsmgr).InnerText;
                    stmt.currency    = nodeStatement.SelectSingleNode("camt:Acct/camt:Ccy", nsmgr).InnerText;

                    Int32 DiffElctrncSeqNb = TAppSettingsManager.GetInt32("DiffElctrncSeqNbFor" + stmt.bankCode + "/" + stmt.accountCode, 0);
                    stmt.id = (Convert.ToInt32(stmt.id) + DiffElctrncSeqNb).ToString();

                    stmt.severalYears = false;
                    XmlNode nm      = nodeStatement.SelectSingleNode("camt:Acct/camt:Ownr/camt:Nm", nsmgr);
                    string  ownName = nm != null?nm.InnerText:
                                      TAppSettingsManager.GetValue("AccountNameFor" + stmt.bankCode + "/" + stmt.accountCode, String.Empty, false);
                    XmlNodeList nodeBalances = nodeStatement.SelectNodes("camt:Bal", nsmgr);

                    foreach (XmlNode nodeBalance in nodeBalances)
                    {
                        // PRCD: PreviouslyClosedBooked
                        if (nodeBalance.SelectSingleNode("camt:Tp/camt:CdOrPrtry/camt:Cd", nsmgr).InnerText == "PRCD")
                        {
                            stmt.startBalance = Decimal.Parse(nodeBalance.SelectSingleNode("camt:Amt", nsmgr).InnerText);

                            // CreditDebitIndicator: CRDT or DBIT for credit or debit
                            if (nodeBalance.SelectSingleNode("camt:CdtDbtInd", nsmgr).InnerText == "DBIT")
                            {
                                stmt.startBalance *= -1.0m;
                            }

                            stmt.date = DateTime.Parse(nodeBalance.SelectSingleNode("camt:Dt", nsmgr).InnerText);
                        }
                        // CLBD: ClosingBooked
                        else if (nodeBalance.SelectSingleNode("camt:Tp/camt:CdOrPrtry/camt:Cd", nsmgr).InnerText == "CLBD")
                        {
                            stmt.endBalance = Decimal.Parse(nodeBalance.SelectSingleNode("camt:Amt", nsmgr).InnerText);

                            // CreditDebitIndicator: CRDT or DBIT for credit or debit
                            if (nodeBalance.SelectSingleNode("camt:CdtDbtInd", nsmgr).InnerText == "DBIT")
                            {
                                stmt.endBalance *= -1.0m;
                            }

                            stmt.date = DateTime.Parse(nodeBalance.SelectSingleNode("camt:Dt", nsmgr).InnerText);
                        }

                        // ITBD: InterimBooked
                        // CLAV: ClosingAvailable
                        // FWAV: ForwardAvailable
                    }

                    string  strDiffBalance = TAppSettingsManager.GetValue("DiffBalanceFor" + stmt.bankCode + "/" + stmt.accountCode, "0");
                    Decimal DiffBalance    = 0.0m;
                    if (Decimal.TryParse(strDiffBalance, out DiffBalance))
                    {
                        stmt.startBalance += DiffBalance;
                        stmt.endBalance   += DiffBalance;
                    }
                    else
                    {
                        TLogging.Log("problem parsing decimal from configuration setting DiffBalanceFor" + stmt.bankCode + "/" + stmt.accountCode);
                    }

                    // if we should parse the transactions only of the past year
                    if (AParsePreviousYear &&
                        stmt.date.Month != 12 &&
                        stmt.date.Day != 31)
                    {
                        stmt.date = new DateTime(stmt.date.Year - 1, 12, 31);
                    }

                    XmlNodeList nodeEntries = nodeStatement.SelectNodes("camt:Ntry", nsmgr);

                    foreach (XmlNode nodeEntry in nodeEntries)
                    {
                        TTransaction tr = new TTransaction();
                        tr.inputDate = DateTime.Parse(nodeEntry.SelectSingleNode("camt:BookgDt/camt:Dt", nsmgr).InnerText);
                        tr.valueDate = DateTime.Parse(nodeEntry.SelectSingleNode("camt:ValDt/camt:Dt", nsmgr).InnerText);

                        if (tr.valueDate.Year != stmt.date.Year)
                        {
                            // ignore transactions that are in a different year than the statement
                            stmt.severalYears = true;
                            continue;
                        }

                        tr.amount = Decimal.Parse(nodeEntry.SelectSingleNode("camt:Amt", nsmgr).InnerText);

                        if (nodeEntry.SelectSingleNode("camt:Amt", nsmgr).Attributes["Ccy"].Value != stmt.currency)
                        {
                            throw new Exception("transaction currency " + nodeEntry.SelectSingleNode("camt:Amt",
                                                                                                     nsmgr).Attributes["Ccy"].Value + " does not match the bank statement currency");
                        }

                        if (nodeEntry.SelectSingleNode("camt:CdtDbtInd", nsmgr).InnerText == "DBIT")
                        {
                            tr.amount *= -1.0m;
                        }

                        XmlNode desc = nodeEntry.SelectSingleNode("camt:NtryDtls/camt:TxDtls/camt:RmtInf/camt:Ustrd", nsmgr);
                        tr.description = desc != null?desc.InnerText:String.Empty;
                        XmlNode partnerName = nodeEntry.SelectSingleNode("camt:NtryDtls/camt:TxDtls/camt:RltdPties/camt:Dbtr/camt:Nm", nsmgr);

                        if (partnerName != null)
                        {
                            tr.partnerName = partnerName.InnerText;
                        }

                        XmlNode accountCode = nodeEntry.SelectSingleNode("camt:NtryDtls/camt:TxDtls/camt:RltdPties/camt:DbtrAcct/camt:Id/camt:IBAN",
                                                                         nsmgr);

                        if (accountCode != null)
                        {
                            tr.accountCode = accountCode.InnerText;
                        }

                        XmlNode CrdtName = nodeEntry.SelectSingleNode("camt:NtryDtls/camt:TxDtls/camt:RltdPties/camt:Cdtr/camt:Nm", nsmgr);
                        XmlNode DbtrName = nodeEntry.SelectSingleNode("camt:NtryDtls/camt:TxDtls/camt:RltdPties/camt:Dbtr/camt:Nm", nsmgr);

                        if ((CrdtName != null) && (CrdtName.InnerText != ownName))
                        {
                            if ((DbtrName != null) && (DbtrName.InnerText == ownName))
                            {
                                // we are the debitor
                            }
                            else if (ownName != String.Empty)
                            {
                                // sometimes donors write the project or recipient in the field where the organisation is supposed to be
                                TLogging.Log("CrdtName is not like expected: " + tr.description + " --- " + CrdtName.InnerText);
                                tr.description += " " + CrdtName.InnerText;
                            }
                        }

                        XmlNode EndToEndId = nodeEntry.SelectSingleNode("camt:NtryDtls/camt:TxDtls/camt:Refs/camt:EndToEndId", nsmgr);

                        if ((EndToEndId != null) &&
                            (EndToEndId.InnerText != "NOTPROVIDED") &&
                            !EndToEndId.InnerText.StartsWith("LS-") &&
                            !EndToEndId.InnerText.StartsWith("ZV") &&
                            !EndToEndId.InnerText.StartsWith("IZV-DISPO"))
                        {
                            // sometimes donors write the project or recipient in unexpected field
                            TLogging.Log("EndToEndId: " + tr.description + " --- " + EndToEndId.InnerText);
                            tr.description += " " + EndToEndId.InnerText;
                        }

                        // eg NSTO+152+00900. look for SEPA Geschäftsvorfallcodes
                        // see the codes: https://www.wgzbank.de/export/sites/wgzbank/de/wgzbank/downloads/produkte_leistungen/firmenkunden/zv_aktuelles/Uebersicht-GVC-und-Buchungstexte-WGZ-BANK_V062015.pdf
                        string[] GVCCode =
                            nodeEntry.SelectSingleNode("camt:NtryDtls/camt:TxDtls/camt:BkTxCd/camt:Prtry/camt:Cd", nsmgr).InnerText.Split(
                                new char[] { '+' });
                        tr.typecode = GVCCode[1];

                        // for SEPA direct debit batches, there are multiple TxDtls records
                        XmlNodeList transactionDetails = nodeEntry.SelectNodes("camt:NtryDtls/camt:TxDtls", nsmgr);

                        if (transactionDetails.Count > 1)
                        {
                            tr.partnerName = String.Empty;
                            tr.description = String.Format(
                                Catalog.GetString("SEPA Sammel-Basislastschrift mit {0} Lastschriften"),
                                transactionDetails.Count);
                        }

                        stmt.transactions.Add(tr);

                        TLogging.LogAtLevel(2, "count : " + stmt.transactions.Count.ToString());
                    }

                    statements.Add(stmt);
                }
            }
            catch (Exception e)
            {
                AVerificationResult.Add(new TVerificationResult(null, "problem with importing camt file; " + e.Message, TResultSeverity.Resv_Critical));
                TLogging.Log("problem with importing camt file; " + e.Message + Environment.NewLine + e.StackTrace);
            }
            finally
            {
                Thread.CurrentThread.CurrentCulture = backupCulture;
            }
        }
Beispiel #3
0
        private void HandleSwiftData(string swiftTag, string swiftData)
        {
            if (swiftTag == "OS")
            {
                // ignore
            }
            else if (swiftTag == "20")
            {
                // 20 is used for each "page" of the statement; but we want to put all transactions together
                // the whole statement closes with 62F
                if (currentStatement == null)
                {
                    currentStatement = new TStatement();
                    // currentStatement.lines.Add(new TLine(swiftTag, swiftData));
                }
            }
            else if (swiftTag == "25")
            {
                int posSlash = swiftData.IndexOf("/");
                currentStatement.bankCode = swiftData.Substring(0, posSlash);
                currentStatement.accountCode = WithoutLeadingZeros(swiftData.Substring(posSlash + 1));
            }
            else if (swiftTag.StartsWith("60"))
            {
                // 60M is the start balance on each page of the statement.
                // 60F is the start balance of the whole statement.

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

                // next 6 characters: YYMMDD
                // next 3 characters: currency
                // last characters: balance with comma for decimal point
                currentStatement.currency = swiftData.Substring(7, 3);
                decimal balance = DebitCreditIndicator * Convert.ToDecimal(swiftData.Substring(10).Replace(",",
                        Thread.CurrentThread.CurrentCulture.NumberFormat.CurrencyDecimalSeparator));

                // we only want to use the first start balance
                if (swiftTag == "60F")
                {
                    currentStatement.startBalance = balance;
                    currentStatement.endBalance = balance;
                }
                else
                {
                    // check if the balance inside the statement is ok
                    // ie it fits the balance of the previous page
                    if (Convert.ToDecimal(Math.Round(currentStatement.endBalance, 2)) != Convert.ToDecimal(balance))
                    {
                        throw new Exception("start balance does not match current balance");
                    }
                }
            }
            else if (swiftTag == "28C")
            {
                // this contains the number of the statement and the number of the page
                // only use for first page
                if (currentStatement.transactions.Count == 0)
                {
                    if (swiftData.IndexOf("/") != -1)
                    {
                        currentStatement.id = swiftData.Substring(0, swiftData.IndexOf("/"));
                    }
                    else
                    {
                        // realtime statement. do not use statement number 0, because Sparkasse has 0/1 for valid statements
                        currentStatement.id = string.Empty;
                    }
                }
            }
            else if (swiftTag == "61")
            {
                TTransaction transaction = new TTransaction();

                // valuta date (YYMMDD)
                try
                {
                    transaction.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);
                    transaction.valueDate = new DateTime(year, month, day);
                }

                swiftData = swiftData.Substring(6);

                if (char.IsDigit(swiftData[0]))
                {
                    // posting date (MMDD)
                    transaction.inputDate = new DateTime(transaction.valueDate.Year,
                        Convert.ToInt32(swiftData.Substring(0, 2)),
                        Convert.ToInt32(swiftData.Substring(2, 2)));
                    swiftData = swiftData.Substring(4);
                }
                else
                {
                    transaction.inputDate = transaction.valueDate;
                }

                // 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
                transaction.amount =
                    debitCreditIndicator * Convert.ToDecimal(swiftData.Substring(0, swiftData.IndexOf("N")).Replace(",",
                            Thread.CurrentThread.CurrentCulture.NumberFormat.CurrencyDecimalSeparator));
                currentStatement.endBalance += transaction.amount;
                swiftData = swiftData.Substring(swiftData.IndexOf("N"));

                // Geschaeftsvorfallcode
                // transaction.typecode = swiftData.Substring(1, 3);
                swiftData = swiftData.Substring(4);

                // the following sub fields are ignored
                // optional: customer reference; ends with //
                // optional: bank reference; ends with CR/LF
                // something else about original currency and transaction fees

                currentStatement.transactions.Add(transaction);
            }
            else if (swiftTag == "86")
            {
                TTransaction transaction = currentStatement.transactions[currentStatement.transactions.Count - 1];

                // Geschaeftsvorfallcode
                transaction.typecode = swiftData.Substring(0, 3);
                swiftData = swiftData.Substring(3);
                char separator = swiftData[0];
                swiftData = swiftData.Substring(1);
                string[] elements = swiftData.Split(new char[] { separator });

                foreach (string element in elements)
                {
                    int key = 0;
                    string value = element;

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

                    if (key == 0)
                    {
                        // Buchungstext
                        transaction.text = value;
                    }
                    else if (key == 10)
                    {
                        // Primanotennummer; ignore at the moment
                    }
                    else if ((key >= 11) && (key <= 19))
                    {
                        // ignore, unknown meaning
                    }
                    else if ((key >= 20) && (key <= 29))
                    {
                        // do not insert a space between description lines
                        transaction.description += value;
                    }
                    else if (key == 30)
                    {
                        transaction.bankCode = value;
                    }
                    else if (key == 31)
                    {
                        // could use WithoutLeadingZeros, but then this must be stored in p_banking_details also without leading zeros
                        // which is not the case at the moment, and would increase complexity of sql queries
                        transaction.accountCode = value;
                    }
                    else if ((key == 32) || (key == 33))
                    {
                        transaction.partnerName += value;
                    }
                    else if (key == 34)
                    {
                        // Textschlüsselergänzung; ignore
                    }
                    else if ((key == 35) || (key == 36))
                    {
                        // Empfängername
                        transaction.description += value;
                    }
                    else if ((key >= 60) && (key <= 63))
                    {
                        transaction.description += value;
                    }
                    else
                    {
                        throw new Exception("unknown key " + key.ToString());
                    }
                }
            }
            else if (swiftTag.StartsWith("62"))
            {
                // 62M: finish page
                // 62F: finish statement
                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
                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 shouldBeBalance = debitCreditIndicator * Convert.ToDecimal(swiftData.Replace(",",
                        Thread.CurrentThread.CurrentCulture.NumberFormat.CurrencyDecimalSeparator));

                currentStatement.endBalance = Math.Round(currentStatement.endBalance, 2);

                if (Convert.ToDecimal(Math.Round(currentStatement.endBalance, 2)) != Convert.ToDecimal(shouldBeBalance))
                {
                    throw new Exception("end balance does not match" +
                        " last transaction was: " + currentStatement.transactions[currentStatement.transactions.Count - 1].partnerName +
                        " balance is: " + currentStatement.endBalance.ToString() +
                        " but should be: " + shouldBeBalance.ToString());
                }

                if (swiftTag == "62F")
                {
                    currentStatement.date = postingDate;
                    statements.Add(currentStatement);
                    currentStatement = null;
                }
            }
            else if (swiftTag == "64")
            {
                // valutensaldo; ignore
            }
            else if (swiftTag == "65")
            {
                // future valutensaldo; ignore
            }
            else
            {
                Console.WriteLine("swiftTag " + swiftTag + " is unknown");
            }
        }