org.kapott.hbci.swift.DTAUS Maven / Gradle / Ivy
Show all versions of hbci4j-adorsys Show documentation
/* $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 lombok.extern.slf4j.Slf4j;
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.structures.Konto;
import org.kapott.hbci.structures.Value;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* 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.
* 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).
* In der Regel wird zunächst ein DTAUS
-Objekt erzeugt. Dazu
* wird der Konstruktor {@link #DTAUS(Konto, int)}
* verwendet, womit gleichzeit das zu verwendende Auftraggeberkonto und der
* Typ des Sammelauftrages (TYPE_CREDIT
für Sammelüberweisungen,
* TYPE_DEBIT
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.
*/
// TODO: API ändern (Setter/Getter), damit wir sauber die LogFilter für
// kritische Daten setzen können
@Slf4j
public class DTAUS {
/**
* 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 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. myAccount
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 blz
,
* number
, curr
und name
richtig
* gesetzt sein.
* execdate
gibt das Datum an, wann dieser Sammelauftrag
* ausgeführt werden soll. ACHTUNG: execdate
wird zur Zeit noch
* nicht ausgewertet!
*
* @param myAccount Gegenkonto für die enthaltenen Aufträge
* @param type TYPE_CREDIT
für Sammelüberweisungen,
* TYPE_DEBIT
für Sammellastschriften
* @param execdate Ausführungsdatum für diesen Sammelauftrag; null
,
* wenn kein Ausführungsdatum gesetzt werden soll (sofortige Ausführung)
*/
public DTAUS(Konto myAccount, int type, Date execdate) {
this.myAccount = myAccount;
this.type = type;
this.execdate = execdate;
entries = new ArrayList<>();
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();
parseDTAUS(dtaus);
}
/**
* Hinzufügen eines einzelnen Auftrages zu diesem Sammelauftrag. Das
* {@link DTAUS.Transaction}-Objekt, welches hier als Argument benötigt wird,
* muss mit 'dtaus.new Transaction()
' erzeugt werden
* ('dtaus
' ist dabei das aktuelle DTAUS
-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 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;
}
/**
* Gibt den Wert von Feld Nr 10 ("Referenznummer des Einreichers") zurück
*/
public String getReferenceId() {
return (this.referenceId != null) ? this.referenceId : "";
}
/**
* Setzt das Feld Nr 10 ("Referennummer des Einreichers")
*/
public void setReferenceId(String referenceId) {
this.referenceId = referenceId;
}
/**
* Rückgabe des Sammelauftrages im DTAUS-Format. Der Rückgabewert dieser
* Methode kann direkt als Parameterwert für den Parameter 'data
'
* bei Sammelaufträgen verwendet werden (für eine Parameterbeschreibung
* siehe Paketbeschreibung des Paketes org.kapott.hbci.GV
).
*
* @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 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, StandardCharsets.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) {
log.debug("parsing DTAUS data");
// 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;
log.debug("SetCLen = " + setCLen + " data bytes (--> " + ((setCLen - 187) / 29.0) + " extensions)");
// "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;
log.debug("field 'nofExtensions' = " + nofExtensions);
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
log.debug("parsinng of DTAUS data finished");
}
/**
* 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 {
/**
* 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.
* Von dem verwendeten {@link Konto}-Objekt müssen mindestens die
* Felder blz
, number
und name
* richtig belegt sein.
*/
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.AbstractHBCIJob#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 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();
}
/**
* Hinzufügen einer Verwendungszweckzeile zu diesem Auftrag.
*/
public void addUsage(String st) {
usage.add(st);
}
/**
* Gibt eine Liste der Verwendungszweckzeilen (String) zurück.
*/
public List 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();
}
}
}