/* $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);
}
}