/* $Id: DTAUS.java,v 1.1 2011/05/04 22:38:03 willuhn Exp $ This file is part of HBCI4Java Copyright (C) 2001-2008 Stefan Palme HBCI4Java is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. HBCI4Java is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package org.kapott.hbci.swift; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.Iterator; import java.util.List; import org.kapott.hbci.datatypes.SyntaxDTAUS; import org.kapott.hbci.exceptions.HBCI_Exception; import org.kapott.hbci.exceptions.InvalidArgumentException; import org.kapott.hbci.exceptions.InvalidUserDataException; import org.kapott.hbci.manager.HBCIUtils; import org.kapott.hbci.manager.LogFilter; import org.kapott.hbci.structures.Konto; import org.kapott.hbci.structures.Value; /** <p>Hilfsklasse zum Erzeugen von DTAUS-Datens�tzen f�r die Verwendung in Sammel�berweisungen und Sammellastschriften. Diese Klasse kann verwendet werden, um den DTAUS-Datenstrom zu erzeugen, der f�r Sammellastschriften und -�berweisungen als Job-Parameter angegeben werden muss.</p> <p>In einem DTAUS-Objekt werden ein oder mehrere Transaktionen gespeichert. Dabei m�ssen alle Transaktionen entweder Lastschriften oder �berweisungen sein. Au�erdem wird f�r alle Transaktionen das gleiche "Auftraggeberkonto" angenommen (bei �berweisungen also das Belastungskonto, bei Lastschriften das Konto, auf das der Betrag gutgeschrieben wird).</p> <p>In der Regel wird zun�chst ein <code>DTAUS</code>-Objekt erzeugt. Dazu wird der Konstruktor {@link #DTAUS(Konto,int)} verwendet, womit gleichzeit das zu verwendende Auftraggeberkonto und der Typ des Sammelauftrages (<code>TYPE_CREDIT</code> f�r Sammel�berweisungen, <code>TYPE_DEBIT</code> f�r Sammellastschriften) festgelegt wird. Anschlie�end k�nnen beliebig viele {@link DTAUS.Transaction}-Objekte erzeugt werden, welche jeweils eine Transaktion darstellen. Jedes so erzeugte Objekt kann mit {@link #addEntry(DTAUS.Transaction)} zum Sammelauftrag hinzugef�gt werden. Die Methode {@link #toString()} liefert schlie�lich den so erzeugten Sammelauftrag im DTAUS-Format.</p> */ // TODO: API �ndern (Setter/Getter), damit wir sauber die LogFilter f�r // kritische Daten setzen k�nnen public class DTAUS { /** Daten einer einzelnen Transaktion, die in einen Sammelauftrag �bernommen werden soll. Vor dem Hinzuf�gen dieser Transaktion zum Sammelauftrag m�ssen alle Felder dieses Transaktions-Objektes mit den jeweiligen Auftragsdaten gef�llt werden. */ public class Transaction { /** <p>Konto des Zahlungsempf�ngers bzw. des Zahlungspflichtigen. Soll dieser Einzelauftrag in eine Sammel�berweisung eingestellt werden, so muss in diesem Feld die Kontoverbindung des Zahlungsempf�ngers eingestellt werden. Bei Sammellastschriften ist hier die Kontoverbindung des Zahlungspflichtigen einzustellen.</p> <p>Von dem verwendeten {@link Konto}-Objekt m�ssen mindestens die Felder <code>blz</code>, <code>number</code> und <code>name</code> richtig belegt sein.</p> */ public Konto otherAccount; /** interne Kunden-ID. Wie die verwendet wird wei� ich leider nicht genau, kann im Prinzip leer gelassen werden (ansonsten Maximall�nge 11 Zeichen). */ public String internalCustomerId; /** Textschl�ssel f�r den Auftrag. Bei Sammel�berweisungen ist dieses Feld mit '51' vorbelegt, bei Sammellastschriften mit '05'. Dieser Wert kann �berschrieben werden, g�ltige Werte finden sich in den Job-Restrictions (siehe {@link org.kapott.hbci.GV.HBCIJob#getJobRestrictions()}). */ public String key; /** Zus�tzlicher Textschl�ssel (wird i.d.R. bankintern verwendet). Dieser Wert muss aus drei Ziffern bestehen und ist mit '000' vorbelegt. Das manuelle Setzen dieses Wertes ist in den meisten F�llen nicht n�tig (au�er f�r Leute, die wissen was sie tun ;-) ). */ public String addkey; /** Geldbetrag, der bei diesem Einzelauftrag �berwiesen (Sammel�berweisungen) bzw. eingezogen (Sammellastschriften) werden soll */ public Value value; private ArrayList<String> usage; /** Erzeugen eine neuen Objektes f�r die Aufnahme von Daten f�r eine Transaktion */ public Transaction() { addkey="000"; key=(type==TYPE_CREDIT?"51":"05"); usage=new ArrayList<String>(); } /** Hinzuf�gen einer Verwendungszweckzeile zu diesem Auftrag. */ public void addUsage(String st) { LogFilter.getInstance().addSecretData(st,"X",LogFilter.FILTER_MOST); usage.add(st); } /** Gibt eine Liste der Verwendungszweckzeilen (String) zur�ck. */ public List<String> getUsage() { return usage; } public String toString() { StringBuffer ret=new StringBuffer(); try { ret.append("0000C"); ret.append(expand(myAccount.blz,8,(byte)0x20,ALIGN_RIGHT)); ret.append(expand(otherAccount.blz,8,(byte)0x20,ALIGN_RIGHT)); ret.append(expand(otherAccount.number,10,(byte)0x30,ALIGN_RIGHT)); sumBLZ+=Long.parseLong(otherAccount.blz); sumNumber+=Long.parseLong(otherAccount.number); if (internalCustomerId==null) { ret.append(expand("",13,(byte)0x30,ALIGN_LEFT)); } else { ret.append((char)0); ret.append(expand(SyntaxDTAUS.check(internalCustomerId),11,(byte)0x30,ALIGN_LEFT)); ret.append((char)0); } ret.append(expand(key,2,(byte)0x30,ALIGN_RIGHT)); ret.append(expand(addkey,3,(byte)0x30,ALIGN_RIGHT)); ret.append((char)0x20); ret.append(expand(Long.toString(value.getCurr().equals("DEM")?value.getLongValue():0),11,(byte)0x30,ALIGN_RIGHT)); ret.append(expand(myAccount.blz,8,(byte)0x20,ALIGN_RIGHT)); ret.append(expand(myAccount.number,10,(byte)0x30,ALIGN_RIGHT)); ret.append(expand(Long.toString(value.getCurr().equals("EUR")?value.getLongValue():0),11,(byte)0x30,ALIGN_RIGHT)); ret.append(expand("",3,(byte)0x20,ALIGN_LEFT)); ret.append(expand(SyntaxDTAUS.check(otherAccount.name),27,(byte)0x20,ALIGN_LEFT)); ret.append(expand("",8,(byte)0x20,ALIGN_LEFT)); if (value.getCurr().equals("EUR")) sumEUR+=value.getLongValue(); else if (value.getCurr().equals("DEM")) sumDM+=value.getLongValue(); ret.append(expand(SyntaxDTAUS.check(myAccount.name),27,(byte)0x20,ALIGN_LEFT)); String st=""; if (usage.size()!=0) st=SyntaxDTAUS.check(usage.get(0)); ret.append(expand(st,27,(byte)0x20,ALIGN_LEFT)); ret.append((char)curr); ret.append(expand("",2,(byte)0x20,ALIGN_LEFT)); int posForNumOfExt=ret.length(); ret.append("00"); int basicLenOfCSet=128+27+27+5; int realLenOfCSet=basicLenOfCSet; int numOfExt=0; // erweiterungsteile // TODO: name2 f�r myAccount und otherAccount vorerst weggelassen for (int i=1;i<usage.size();i++) { st=SyntaxDTAUS.check(usage.get(i)); if (((realLenOfCSet%128)+29)>128) { int diff=128-(realLenOfCSet%128); ret.append(expand("",diff,(byte)0x20,ALIGN_LEFT)); realLenOfCSet+=diff; } ret.append("02"); ret.append(expand(st,27,(byte)0x20,ALIGN_LEFT)); realLenOfCSet+=29; numOfExt++; } if ((realLenOfCSet%128)!=0) { int diff=128-(realLenOfCSet%128); ret.append(expand("",diff,(byte)0x20,ALIGN_LEFT)); realLenOfCSet+=diff; } ret.replace(posForNumOfExt,posForNumOfExt+2,expand(Integer.toString(numOfExt),2,(byte)0x30,ALIGN_RIGHT)); ret.replace(0,4,expand(Integer.toString(basicLenOfCSet+29*numOfExt),4,(byte)0x30,ALIGN_RIGHT)); } catch (NullPointerException e) { throw new HBCI_Exception("probably one or more DTAUS values which MUST be set are null - please refer the API doc", e); } return ret.toString(); } } /** Typ des Sammelauftrages: Sammel�berweisung */ public static final int TYPE_CREDIT=1; /** Typ des Sammelauftrages: Sammellastschrift */ public static final int TYPE_DEBIT=2; /** TODO: doku fehlt */ public static final byte CURR_DM=0x20; /** TODO: doku fehlt */ public static final byte CURR_EUR=0x31; private static final byte ALIGN_LEFT=1; private static final byte ALIGN_RIGHT=2; private Konto myAccount; private int type; private Date execdate; private byte curr; private String referenceId; private ArrayList<Transaction> entries; private long sumDM; private long sumEUR; private long sumBLZ; private long sumNumber; /** Entspricht {@link #DTAUS(Konto, int, Date) DTAUS(myAccount,type,null)} */ public DTAUS(Konto myAccount,int type) { this(myAccount,type,null); } /** Erzeugen eines neuen Objektes f�r die Aufnahme von Sammelauftr�gen. <code>myAccount</code> ist dabei das "eigene" Konto, welches bei Sammel�berweisungen als Belastungskonto und bei Sammellastschriften als Gutschriftkonto verwendet wird. Von dem {@link Konto}-Objekt m�ssen mindestens die Felder <code>blz</code>, <code>number</code>, <code>curr</code> und <code>name</code> richtig gesetzt sein. <br/> <code>execdate</code> gibt das Datum an, wann dieser Sammelauftrag ausgef�hrt werden soll. ACHTUNG: <code>execdate</code> wird zur Zeit noch nicht ausgewertet! @param myAccount Gegenkonto f�r die enthaltenen Auftr�ge @param type <ul><li><code>TYPE_CREDIT</code> f�r Sammel�berweisungen,</li> <li><code>TYPE_DEBIT</code> f�r Sammellastschriften</li></ul> @param execdate Ausf�hrungsdatum f�r diesen Sammelauftrag; <code>null</code>, wenn kein Ausf�hrungsdatum gesetzt werden soll (sofortige Ausf�hrung) */ public DTAUS(Konto myAccount,int type,Date execdate) { LogFilter.getInstance().addSecretData(myAccount.blz,"X",LogFilter.FILTER_MOST); LogFilter.getInstance().addSecretData(myAccount.customerid,"X",LogFilter.FILTER_IDS); LogFilter.getInstance().addSecretData(myAccount.name,"X",LogFilter.FILTER_IDS); LogFilter.getInstance().addSecretData(myAccount.name2,"X",LogFilter.FILTER_IDS); LogFilter.getInstance().addSecretData(myAccount.number,"X",LogFilter.FILTER_IDS); LogFilter.getInstance().addSecretData(myAccount.subnumber,"X",LogFilter.FILTER_MOST); this.myAccount=myAccount; this.type=type; this.execdate=execdate; entries=new ArrayList<Transaction>(); if (myAccount.curr.equals("EUR")) this.curr=CURR_EUR; else if (myAccount.curr.equals("DEM")) this.curr=CURR_DM; else throw new InvalidUserDataException("*** invalid currency of this account: "+myAccount.curr); } /** TODO: doku fehlt */ public DTAUS(String dtaus) { entries=new ArrayList<Transaction>(); parseDTAUS(dtaus); } /** Hinzuf�gen eines einzelnen Auftrages zu diesem Sammelauftrag. Das {@link DTAUS.Transaction}-Objekt, welches hier als Argument ben�tigt wird, muss mit '<code>dtaus.new Transaction()</code>' erzeugt werden ('<code>dtaus</code>' ist dabei das aktuelle <code>DTAUS</code>-Objekt). @param entry Hinzuzuf�gender Einzelauftrag */ public void addEntry(Transaction entry) { entries.add(entry); } /** TODO: doku fehlt */ public byte getCurr() { return curr; } /** TODO: doku fehlt */ public ArrayList<Transaction> getEntries() { return entries; } /** TODO: doku fehlt */ public Date getExecdate() { return execdate; } /** TODO: doku fehlt */ public Konto getMyAccount() { return myAccount; } /** TODO: doku fehlt */ public int getType() { return type; } /** Setzt das Feld Nr 10 ("Referennummer des Einreichers") */ public void setReferenceId(String referenceId) { this.referenceId = referenceId; } /** Gibt den Wert von Feld Nr 10 ("Referenznummer des Einreichers") zur�ck */ public String getReferenceId() { return (this.referenceId!=null)?this.referenceId:""; } /** R�ckgabe des Sammelauftrages im DTAUS-Format. Der R�ckgabewert dieser Methode kann direkt als Parameterwert f�r den Parameter '<code>data</code>' bei Sammelauftr�gen verwendet werden (f�r eine Parameterbeschreibung siehe Paketbeschreibung des Paketes <code>org.kapott.hbci.GV</code>). @return DTAUS-Datenstrom f�r diesen Sammelauftrag */ public String toString() { StringBuffer ret=new StringBuffer(); sumBLZ=0; sumNumber=0; sumDM=0; sumEUR=0; // A-set ret.append("0128A"); switch (type) { case TYPE_CREDIT: ret.append("GK"); break; case TYPE_DEBIT: ret.append("LK"); break; default: throw new InvalidUserDataException("*** type of DTAUS order not set (DEBIT/CREDIT)"); } ret.append(expand(myAccount.blz,8,(byte)0x20,ALIGN_RIGHT)); ret.append(expand("",8,(byte)0x30,ALIGN_LEFT)); ret.append(expand(SyntaxDTAUS.check(myAccount.name),27,(byte)0x20,ALIGN_LEFT)); SimpleDateFormat form=new SimpleDateFormat("ddMMyy"); ret.append(form.format(new Date())); ret.append(expand("", 4, (byte)0x20, ALIGN_LEFT)); ret.append(expand(myAccount.number, 10, (byte)0x30, ALIGN_RIGHT)); ret.append(expand(getReferenceId(), 10, (byte)0x30, ALIGN_RIGHT)); ret.append(expand("", 15, (byte)0x20, ALIGN_LEFT)); if (execdate==null) { ret.append(expand("",8,(byte)0x20,ALIGN_LEFT)); } else { form=new SimpleDateFormat("ddMMyyyy"); ret.append(form.format(execdate)); } ret.append(expand("",24,(byte)0x20,ALIGN_LEFT)); ret.append((char)curr); // C-sets for (Iterator<Transaction> i=entries.iterator();i.hasNext();) { Transaction entry= i.next(); ret.append(entry.toString()); } // E-set ret.append("0128E"); ret.append(expand("",5,(byte)0x20,ALIGN_LEFT)); ret.append(expand(Integer.toString(entries.size()),7,(byte)0x30,ALIGN_RIGHT)); ret.append(expand(Long.toString(curr==CURR_DM?sumDM:0), 13,(byte)0x30,ALIGN_RIGHT)); ret.append(expand(Long.toString(sumNumber),17,(byte)0x30,ALIGN_RIGHT)); ret.append(expand(Long.toString(sumBLZ),17,(byte)0x30,ALIGN_RIGHT)); ret.append(expand(Long.toString(curr==CURR_EUR?sumEUR:0), 13,(byte)0x30,ALIGN_RIGHT)); ret.append(expand("",51,(byte)0x20,ALIGN_LEFT)); return ret.toString(); } private String expand(String st,int len,byte filler,int align) { if (st.length()<len) { try { byte[] fill=new byte[len-st.length()]; Arrays.fill(fill,filler); String fillst=new String(fill,"ISO-8859-1"); if (align==ALIGN_LEFT) st=st+fillst; else if (align==ALIGN_RIGHT) st=fillst+st; else throw new HBCI_Exception("*** invalid align type: "+align); } catch (Exception e) { throw new HBCI_Exception(e); } } else if (st.length()>len) { throw new InvalidArgumentException("*** string too long: \""+st+"\" has "+st.length()+" chars, but max is "+len); } return st; } private void parseDTAUS(String dtaus) { HBCIUtils.log("parsing DTAUS data",HBCIUtils.LOG_DEBUG); // satz A String header=dtaus.substring(0,5); if (!header.equals("0128A")) { throw new HBCI_Exception("*** DTAUS stream does not start with '0128A'"); } char typ=dtaus.charAt(5); if (typ=='G') { this.type=TYPE_CREDIT; } else if (typ=='L') { this.type=TYPE_DEBIT; } else { throw new HBCI_Exception("*** Invalid type: "+typ); } setReferenceId(dtaus.substring(70, 80).trim()); String myBLZ=dtaus.substring(7,15).trim(); String myName=dtaus.substring(23,50).trim(); String myNumber=dtaus.substring(60,70).trim(); try { SimpleDateFormat format=new SimpleDateFormat("ddMMyyyy"); this.execdate=format.parse(dtaus.substring(95,103).trim()); } catch (ParseException e) { this.execdate=null; } this.curr=(byte)dtaus.charAt(127); if (this.curr!=CURR_DM && this.curr!=CURR_EUR) { throw new HBCI_Exception("*** Invalid currency: "+this.curr); } this.myAccount=new Konto("DE",myBLZ,myNumber); this.myAccount.curr=(this.curr==CURR_EUR)?"EUR":"DEM"; this.myAccount.name=myName; // satz C beginn int posi=128; // schleife f�r einzelne auftr�ge (c-sets) while (true) { Transaction entry=new Transaction(); if (dtaus.charAt(posi+4)!='C') { // gefundener abschnitt ist kein c-set break; } int setCLen=Integer.parseInt(dtaus.substring(posi,posi+4)); posi+=4; HBCIUtils.log("SetCLen = "+setCLen+" data bytes (--> "+((setCLen-187)/29.0)+" extensions)", HBCIUtils.LOG_DEBUG); // "C" �berspringen posi++; // skip myBLZ posi+=8; String otherBLZ=dtaus.substring(posi,posi+8).trim(); posi+=8; String otherNumber=dtaus.substring(posi,posi+10).trim(); posi+=10; entry.internalCustomerId=dtaus.substring(posi+1,posi+1+11).trim(); posi+=13; entry.key=dtaus.substring(posi,posi+2).trim(); posi+=2; entry.addkey=dtaus.substring(posi,posi+3).trim(); posi+=3; // skip bankintern posi++; String value_st=null; if (this.curr==CURR_EUR) { value_st=dtaus.substring(posi+29,posi+29+11).trim(); } else { value_st=dtaus.substring(posi,posi+11).trim(); } posi+=40; // skip reserve posi+=3; String otherName=dtaus.substring(posi,posi+27).trim(); posi+=27; // skip fillbytes posi+=8; // skip myName posi+=27; entry.addUsage(dtaus.substring(posi,posi+27).trim()); posi+=27; // skip w�hrung // TODO: hier konsistenz �berpr�fen posi++; // skip reserve posi+=2; int nofExtensions=Integer.parseInt(dtaus.substring(posi,posi+2)); posi+=2; HBCIUtils.log("field 'nofExtensions' = "+nofExtensions,HBCIUtils.LOG_DEBUG); String otherName2=null; for (int i=0;i<nofExtensions;i++) { if ((posi%128)+29 > 128) { posi=((posi/128)+1)*128; } String code=dtaus.substring(posi,posi+2).trim(); posi+=2; String data=dtaus.substring(posi,posi+27).trim(); posi+=27; if (code.equals("01")) { otherName2=data; } else if (code.equals("02")) { entry.addUsage(data); } else if (code.equals("03")) { this.myAccount.name2=data; } } posi=((posi/128)+1)*128; entry.otherAccount=new Konto("DE",otherBLZ,otherNumber); entry.otherAccount.curr=(this.curr==CURR_EUR)?"EUR":"DEM"; entry.otherAccount.name=otherName; entry.otherAccount.name2=otherName2; entry.value=new Value(Long.parseLong(value_st), (this.curr==CURR_EUR)?"EUR":"DEM"); addEntry(entry); } // e-satz if (!dtaus.substring(posi,posi+5).equals("0128E")) { throw new HBCI_Exception("*** e-set does not start with 0128E"); } posi+=5; // skip reserve posi+=5; int x=Integer.parseInt(dtaus.substring(posi,posi+7)); if (x!=entries.size()) { throw new HBCI_Exception("*** there were "+entries.size()+" c-sets, but e-set says "+x); } // TODO: restliche konsistenzchecks machen HBCIUtils.log("parsinng of DTAUS data finished", HBCIUtils.LOG_DEBUG); } }