Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 27 additions & 2 deletions src/main/java/org/kopi/ebics/client/EbicsClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
import org.kopi.ebics.session.OrderType;
import org.kopi.ebics.session.Product;
import org.kopi.ebics.utils.Constants;
import org.kopi.ebics.xml.EbicsXmlFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -413,6 +414,17 @@ public void sendFile(File file, EbicsOrderType orderType) throws Exception {

public void fetchFile(File file, User user, Product product, EbicsOrderType orderType,
boolean isTest) throws IOException, EbicsException {
fetchFile(file, user, product, orderType, null, isTest);
}

/**
* Downloads a file from the bank.
*
* @param downloadParams optional EBICS 3.0 service parameters and report period; with a
* service name set the order is sent as a BTD business transaction format order
*/
public void fetchFile(File file, User user, Product product, EbicsOrderType orderType,
EbicsDownloadParams downloadParams, boolean isTest) throws IOException, EbicsException {
FileTransfer transferManager;
EbicsSession session = createSession(user, product);
session.addSessionParam("FORMAT", "pain.xxx.cfonb160.dct");
Expand All @@ -425,7 +437,7 @@ public void fetchFile(File file, User user, Product product, EbicsOrderType orde
configuration.getTransferTraceDirectory(user));

try {
transferManager.fetchFile(orderType, file);
transferManager.fetchFile(orderType, downloadParams, file);
} catch (NoDownloadDataAvailableException e) {
// don't log this exception as an error, caller can decide how to handle
throw e;
Expand All @@ -435,9 +447,22 @@ public void fetchFile(File file, User user, Product product, EbicsOrderType orde
}
}

/**
* Downloads a file for a report period.
*
* <p><b>A {@link Date} is an instant, the EBICS report period is a pair of calendar days.</b>
* The calendar day is therefore read in the timezone of the machine running this code, so a
* {@code Date} at UTC midnight becomes the previous day in any zone west of UTC. Prefer
* {@link #fetchFile(File, User, Product, EbicsOrderType, EbicsDownloadParams, boolean)} with
* {@link java.time.LocalDate} values, which has no timezone in it.
*/
public void fetchFile(File file, EbicsOrderType orderType, Date start, Date end) throws IOException,
EbicsException {
fetchFile(file, defaultUser, defaultProduct, orderType, false);
fetchFile(file, defaultUser, defaultProduct, orderType,
EbicsDownloadParams.dateRangeOnly(
start == null ? null : EbicsXmlFactory.toLocalDate(start),
end == null ? null : EbicsXmlFactory.toLocalDate(end)),
false);
}

/**
Expand Down
56 changes: 56 additions & 0 deletions src/main/java/org/kopi/ebics/client/EbicsDownloadParams.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package org.kopi.ebics.client;

import java.time.LocalDate;

/**
* Service parameters for an EBICS 3.0 (H005) BTD download order.
*
* <p>With a {@code serviceName} set, the request is sent as {@code AdminOrderType=BTD} with a
* {@code BTDOrderParams/Service} block. With {@code serviceName} left {@code null}, only the
* optional date range is applied and the legacy EBICS 2.x order type is kept, so existing
* callers keep their behaviour.
*
* <p>The report period is a pair of calendar days ({@link LocalDate}), not instants: EBICS sends
* it as {@code xs:date}, and a timezone in that position only creates off-by-one-day bugs.
*
* <p>The constructor rejects a partial or reversed range. Both would otherwise travel silently:
* a half range is dropped when the request is built, and a reversed one is schema-valid and comes
* back as "no data available", which is indistinguishable from a period that really was empty.
* This is the single place every caller passes through, so the check lives here rather than in
* each caller.
*/
public record EbicsDownloadParams(
String serviceName,
String scope,
String option,
String messageName,
String messageVersion,
String containerType,
LocalDate startDate,
LocalDate endDate) {

public EbicsDownloadParams {
if ((startDate == null) != (endDate == null)) {
throw new IllegalArgumentException(
"startDate and endDate must be given together (--start/--end); a single one"
+ " would be dropped from the bank request");
}
if (startDate != null && endDate.isBefore(startDate)) {
throw new IllegalArgumentException(
"endDate must not be before startDate, got " + startDate + " to " + endDate);
}
}

/** Date-range-only parameters for the legacy (non-BTD) download path. */
public static EbicsDownloadParams dateRangeOnly(LocalDate startDate, LocalDate endDate) {
if (startDate == null && endDate == null) {
return null;
}
return new EbicsDownloadParams(null, null, null, null, null, null, startDate, endDate);
}

/** Whether these parameters describe an EBICS 3.0 BTD business transaction format order. */
public boolean isBtd() {
return serviceName != null;
}
}
20 changes: 19 additions & 1 deletion src/main/java/org/kopi/ebics/client/FileTransfer.java
Original file line number Diff line number Diff line change
Expand Up @@ -173,9 +173,27 @@ public void sendFile(ContentFactory factory,
public void fetchFile(EbicsOrderType orderType,
File outputFile)
throws IOException, EbicsException
{
fetchFile(orderType, null, outputFile);
}

/**
* Fetches a file of the given order type from the bank.
* This type of transfer will run until everything is processed.
* No transaction recovery is possible.
* @param orderType type of file to fetch
* @param downloadParams optional EBICS 3.0 service parameters and report period
* @param outputFile where to put the data
* @throws IOException communication error
* @throws EbicsException server generated error
*/
public void fetchFile(EbicsOrderType orderType,
EbicsDownloadParams downloadParams,
File outputFile)
throws IOException, EbicsException
{
var sender = new HttpRequestSender(session);
var initializer = new DownloadInitializationRequestElement(session, orderType);
var initializer = new DownloadInitializationRequestElement(session, orderType, downloadParams);
initializer.build();
initializer.validate();

Expand Down
Loading
Loading