feat(workshop): add reference implementation for DKP currency conversion
Provide reference implementation for custom DukePoints (DKP) currency including local ExchangeRateProvider and bidirectional conversion between CHF and DKP. Signed-off-by: Marcus Fihlon <marcus@fihlon.swiss>
This commit is contained in:
parent
f307005c6d
commit
1c711a077e
3 changed files with 234 additions and 15 deletions
|
|
@ -1,18 +1,30 @@
|
||||||
package swiss.fihlon.workshop.money.part4;
|
package swiss.fihlon.workshop.money.part4;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
import javax.money.convert.ConversionQuery;
|
import javax.money.convert.ConversionQuery;
|
||||||
|
import javax.money.convert.ConversionContext;
|
||||||
|
import javax.money.convert.ConversionContextBuilder;
|
||||||
import javax.money.convert.CurrencyConversion;
|
import javax.money.convert.CurrencyConversion;
|
||||||
import javax.money.convert.ExchangeRate;
|
import javax.money.convert.ExchangeRate;
|
||||||
import javax.money.convert.ExchangeRateProvider;
|
import javax.money.convert.ExchangeRateProvider;
|
||||||
import javax.money.convert.ProviderContext;
|
import javax.money.convert.ProviderContext;
|
||||||
|
import javax.money.convert.ProviderContextBuilder;
|
||||||
|
import javax.money.convert.RateType;
|
||||||
|
import org.javamoney.moneta.convert.ExchangeRateBuilder;
|
||||||
|
import org.javamoney.moneta.spi.DefaultNumberValue;
|
||||||
|
import org.javamoney.moneta.spi.LazyBoundCurrencyConversion;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Bonus exercise 1 for implementing a local CSV-based exchange rate provider with JSR-354.</p>
|
* <p>Bonus exercise 1 for implementing a local CSV-based exchange rate provider with JSR-354.</p>
|
||||||
*
|
*
|
||||||
* <p>The provider is intended for workshop usage and should read rates from the classpath resource
|
* <p>The provider is intended for workshop usage and should read rates from the classpath resource
|
||||||
* {@code exchange-rates.csv}.</p>
|
* {@code exchange-rates.csv}.</p>
|
||||||
*
|
|
||||||
* <p>The implementation is intentionally incomplete and should be finished by making tests pass.</p>
|
|
||||||
*/
|
*/
|
||||||
public class CsvExchangeRateProvider implements ExchangeRateProvider {
|
public class CsvExchangeRateProvider implements ExchangeRateProvider {
|
||||||
|
|
||||||
|
|
@ -20,6 +32,10 @@ public class CsvExchangeRateProvider implements ExchangeRateProvider {
|
||||||
* <p>Name of the classpath CSV resource containing exchange rates.</p>
|
* <p>Name of the classpath CSV resource containing exchange rates.</p>
|
||||||
*/
|
*/
|
||||||
public static final String RESOURCE_NAME = "exchange-rates.csv";
|
public static final String RESOURCE_NAME = "exchange-rates.csv";
|
||||||
|
private static final ProviderContext CONTEXT =
|
||||||
|
ProviderContextBuilder.of("csv-exchange-rate-provider", RateType.HISTORIC).build();
|
||||||
|
|
||||||
|
private final Map<CurrencyPair, BigDecimal> ratesByPair;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Create a new provider instance.</p>
|
* <p>Create a new provider instance.</p>
|
||||||
|
|
@ -27,6 +43,7 @@ public class CsvExchangeRateProvider implements ExchangeRateProvider {
|
||||||
* <p>The provider should load exchange rates from {@link #RESOURCE_NAME}.</p>
|
* <p>The provider should load exchange rates from {@link #RESOURCE_NAME}.</p>
|
||||||
*/
|
*/
|
||||||
public CsvExchangeRateProvider() {
|
public CsvExchangeRateProvider() {
|
||||||
|
this.ratesByPair = loadRates();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -36,7 +53,7 @@ public class CsvExchangeRateProvider implements ExchangeRateProvider {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public ProviderContext getContext() {
|
public ProviderContext getContext() {
|
||||||
throw new UnsupportedOperationException("TODO");
|
return CONTEXT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -47,7 +64,16 @@ public class CsvExchangeRateProvider implements ExchangeRateProvider {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public ExchangeRate getExchangeRate(ConversionQuery conversionQuery) {
|
public ExchangeRate getExchangeRate(ConversionQuery conversionQuery) {
|
||||||
throw new UnsupportedOperationException("TODO");
|
CurrencyPair currencyPair = CurrencyPair.from(conversionQuery);
|
||||||
|
BigDecimal factor = ratesByPair.get(currencyPair);
|
||||||
|
if (factor == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new ExchangeRateBuilder(getContext().getProviderName(), RateType.DEFERRED)
|
||||||
|
.setBase(conversionQuery.getBaseCurrency())
|
||||||
|
.setTerm(conversionQuery.getCurrency())
|
||||||
|
.setFactor(DefaultNumberValue.of(factor))
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -60,6 +86,60 @@ public class CsvExchangeRateProvider implements ExchangeRateProvider {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public CurrencyConversion getCurrencyConversion(ConversionQuery conversionQuery) {
|
public CurrencyConversion getCurrencyConversion(ConversionQuery conversionQuery) {
|
||||||
throw new UnsupportedOperationException("TODO");
|
if (getExchangeRate(conversionQuery) == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
ConversionContext conversionContext = ConversionContextBuilder.create(getContext(), RateType.DEFERRED).build();
|
||||||
|
return new LazyBoundCurrencyConversion(conversionQuery, this, conversionContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Load exchange rates from the classpath CSV resource into a lookup map.</p>
|
||||||
|
*
|
||||||
|
* <p>The CSV is expected to contain a header and records in the format
|
||||||
|
* {@code base,term,rate}.</p>
|
||||||
|
*
|
||||||
|
* @return map keyed by currency pair containing the configured exchange factor
|
||||||
|
*/
|
||||||
|
private static Map<CurrencyPair, BigDecimal> loadRates() {
|
||||||
|
InputStream inputStream = CsvExchangeRateProvider.class.getClassLoader().getResourceAsStream(RESOURCE_NAME);
|
||||||
|
if (inputStream == null) {
|
||||||
|
throw new IllegalStateException("Could not load classpath resource: " + RESOURCE_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
|
||||||
|
Map<CurrencyPair, BigDecimal> rates = new HashMap<>();
|
||||||
|
reader.readLine();
|
||||||
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
String[] values = line.split(",");
|
||||||
|
CurrencyPair currencyPair = new CurrencyPair(values[0], values[1]);
|
||||||
|
BigDecimal factor = new BigDecimal(values[2]);
|
||||||
|
rates.put(currencyPair, factor);
|
||||||
|
}
|
||||||
|
return rates;
|
||||||
|
} catch (Exception exception) {
|
||||||
|
throw new IllegalStateException("Failed to parse exchange rates from: " + RESOURCE_NAME, exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Simple key for identifying an exchange rate by base and term currency code.</p>
|
||||||
|
*
|
||||||
|
* @param baseCurrencyCode ISO currency code of the base currency
|
||||||
|
* @param termCurrencyCode ISO currency code of the term currency
|
||||||
|
*/
|
||||||
|
private record CurrencyPair(String baseCurrencyCode, String termCurrencyCode) {
|
||||||
|
/**
|
||||||
|
* <p>Create a currency-pair key from a conversion query.</p>
|
||||||
|
*
|
||||||
|
* @param conversionQuery query containing base and term currency
|
||||||
|
* @return key for map-based rate lookup
|
||||||
|
*/
|
||||||
|
private static CurrencyPair from(ConversionQuery conversionQuery) {
|
||||||
|
return new CurrencyPair(
|
||||||
|
conversionQuery.getBaseCurrency().getCurrencyCode(),
|
||||||
|
conversionQuery.getCurrency().getCurrencyCode());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,27 @@
|
||||||
package swiss.fihlon.workshop.money.part4;
|
package swiss.fihlon.workshop.money.part4;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.NavigableMap;
|
||||||
|
import java.util.TreeMap;
|
||||||
import javax.money.convert.ConversionQuery;
|
import javax.money.convert.ConversionQuery;
|
||||||
|
import javax.money.convert.ConversionContext;
|
||||||
|
import javax.money.convert.ConversionContextBuilder;
|
||||||
import javax.money.convert.CurrencyConversion;
|
import javax.money.convert.CurrencyConversion;
|
||||||
import javax.money.convert.ExchangeRate;
|
import javax.money.convert.ExchangeRate;
|
||||||
import javax.money.convert.ExchangeRateProvider;
|
import javax.money.convert.ExchangeRateProvider;
|
||||||
import javax.money.convert.ProviderContext;
|
import javax.money.convert.ProviderContext;
|
||||||
|
import javax.money.convert.ProviderContextBuilder;
|
||||||
|
import javax.money.convert.RateType;
|
||||||
|
import org.javamoney.moneta.convert.ExchangeRateBuilder;
|
||||||
|
import org.javamoney.moneta.spi.DefaultNumberValue;
|
||||||
|
import org.javamoney.moneta.spi.LazyBoundCurrencyConversion;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Bonus exercise 2 for implementing a local CSV-based historical exchange rate provider with JSR-354.</p>
|
* <p>Bonus exercise 2 for implementing a local CSV-based historical exchange rate provider with JSR-354.</p>
|
||||||
|
|
@ -14,8 +30,6 @@ import javax.money.convert.ProviderContext;
|
||||||
* {@code exchange-rates-hist.csv}.</p>
|
* {@code exchange-rates-hist.csv}.</p>
|
||||||
*
|
*
|
||||||
* <p>The lookup should use base currency, term currency, and a date passed in the query context.</p>
|
* <p>The lookup should use base currency, term currency, and a date passed in the query context.</p>
|
||||||
*
|
|
||||||
* <p>The implementation is intentionally incomplete and should be finished by making tests pass.</p>
|
|
||||||
*/
|
*/
|
||||||
public class CsvHistoricalExchangeRateProvider implements ExchangeRateProvider {
|
public class CsvHistoricalExchangeRateProvider implements ExchangeRateProvider {
|
||||||
|
|
||||||
|
|
@ -28,6 +42,10 @@ public class CsvHistoricalExchangeRateProvider implements ExchangeRateProvider {
|
||||||
* <p>Query key type for historical lookups.</p>
|
* <p>Query key type for historical lookups.</p>
|
||||||
*/
|
*/
|
||||||
public static final Class<LocalDate> DATE_QUERY_KEY = LocalDate.class;
|
public static final Class<LocalDate> DATE_QUERY_KEY = LocalDate.class;
|
||||||
|
private static final ProviderContext CONTEXT =
|
||||||
|
ProviderContextBuilder.of("csv-historical-exchange-rate-provider", RateType.HISTORIC).build();
|
||||||
|
|
||||||
|
private final Map<CurrencyPair, NavigableMap<LocalDate, BigDecimal>> ratesByPairAndDate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Create a new provider instance.</p>
|
* <p>Create a new provider instance.</p>
|
||||||
|
|
@ -35,6 +53,7 @@ public class CsvHistoricalExchangeRateProvider implements ExchangeRateProvider {
|
||||||
* <p>The provider should load exchange rates from {@link #RESOURCE_NAME}.</p>
|
* <p>The provider should load exchange rates from {@link #RESOURCE_NAME}.</p>
|
||||||
*/
|
*/
|
||||||
public CsvHistoricalExchangeRateProvider() {
|
public CsvHistoricalExchangeRateProvider() {
|
||||||
|
this.ratesByPairAndDate = loadRates();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -44,7 +63,7 @@ public class CsvHistoricalExchangeRateProvider implements ExchangeRateProvider {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public ProviderContext getContext() {
|
public ProviderContext getContext() {
|
||||||
throw new UnsupportedOperationException("TODO");
|
return CONTEXT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -62,7 +81,27 @@ public class CsvHistoricalExchangeRateProvider implements ExchangeRateProvider {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public ExchangeRate getExchangeRate(ConversionQuery conversionQuery) {
|
public ExchangeRate getExchangeRate(ConversionQuery conversionQuery) {
|
||||||
throw new UnsupportedOperationException("TODO");
|
LocalDate requestedDate = conversionQuery.get(DATE_QUERY_KEY);
|
||||||
|
if (requestedDate == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
CurrencyPair currencyPair = CurrencyPair.from(conversionQuery);
|
||||||
|
NavigableMap<LocalDate, BigDecimal> ratesByDate = ratesByPairAndDate.get(currencyPair);
|
||||||
|
if (ratesByDate == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map.Entry<LocalDate, BigDecimal> bestMatch = ratesByDate.floorEntry(requestedDate);
|
||||||
|
if (bestMatch == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ExchangeRateBuilder(getContext().getProviderName(), RateType.HISTORIC)
|
||||||
|
.setBase(conversionQuery.getBaseCurrency())
|
||||||
|
.setTerm(conversionQuery.getCurrency())
|
||||||
|
.setFactor(DefaultNumberValue.of(bestMatch.getValue()))
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -78,6 +117,61 @@ public class CsvHistoricalExchangeRateProvider implements ExchangeRateProvider {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public CurrencyConversion getCurrencyConversion(ConversionQuery conversionQuery) {
|
public CurrencyConversion getCurrencyConversion(ConversionQuery conversionQuery) {
|
||||||
throw new UnsupportedOperationException("TODO");
|
if (getExchangeRate(conversionQuery) == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
ConversionContext conversionContext = ConversionContextBuilder.create(getContext(), RateType.HISTORIC).build();
|
||||||
|
return new LazyBoundCurrencyConversion(conversionQuery, this, conversionContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Load historical exchange rates from the classpath CSV resource into a date-indexed lookup map.</p>
|
||||||
|
*
|
||||||
|
* <p>The CSV is expected to contain a header and records in the format
|
||||||
|
* {@code date,base,term,rate}.</p>
|
||||||
|
*
|
||||||
|
* @return map keyed by currency pair with a navigable date-to-rate mapping
|
||||||
|
*/
|
||||||
|
private static Map<CurrencyPair, NavigableMap<LocalDate, BigDecimal>> loadRates() {
|
||||||
|
InputStream inputStream = CsvHistoricalExchangeRateProvider.class.getClassLoader().getResourceAsStream(RESOURCE_NAME);
|
||||||
|
if (inputStream == null) {
|
||||||
|
throw new IllegalStateException("Could not load classpath resource: " + RESOURCE_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
|
||||||
|
Map<CurrencyPair, NavigableMap<LocalDate, BigDecimal>> rates = new HashMap<>();
|
||||||
|
reader.readLine();
|
||||||
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
String[] values = line.split(",");
|
||||||
|
LocalDate date = LocalDate.parse(values[0]);
|
||||||
|
CurrencyPair currencyPair = new CurrencyPair(values[1], values[2]);
|
||||||
|
BigDecimal factor = new BigDecimal(values[3]);
|
||||||
|
rates.computeIfAbsent(currencyPair, key -> new TreeMap<>()).put(date, factor);
|
||||||
|
}
|
||||||
|
return rates;
|
||||||
|
} catch (Exception exception) {
|
||||||
|
throw new IllegalStateException("Failed to parse historical exchange rates from: " + RESOURCE_NAME, exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Simple key for identifying a historical exchange-rate series by base and term currency code.</p>
|
||||||
|
*
|
||||||
|
* @param baseCurrencyCode ISO currency code of the base currency
|
||||||
|
* @param termCurrencyCode ISO currency code of the term currency
|
||||||
|
*/
|
||||||
|
private record CurrencyPair(String baseCurrencyCode, String termCurrencyCode) {
|
||||||
|
/**
|
||||||
|
* <p>Create a currency-pair key from a conversion query.</p>
|
||||||
|
*
|
||||||
|
* @param conversionQuery query containing base and term currency
|
||||||
|
* @return key for map-based historical rate lookup
|
||||||
|
*/
|
||||||
|
private static CurrencyPair from(ConversionQuery conversionQuery) {
|
||||||
|
return new CurrencyPair(
|
||||||
|
conversionQuery.getBaseCurrency().getCurrencyCode(),
|
||||||
|
conversionQuery.getCurrency().getCurrencyCode());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,32 @@
|
||||||
package swiss.fihlon.workshop.money.part4;
|
package swiss.fihlon.workshop.money.part4;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
import javax.money.convert.ConversionQuery;
|
import javax.money.convert.ConversionQuery;
|
||||||
|
import javax.money.convert.ConversionContext;
|
||||||
|
import javax.money.convert.ConversionContextBuilder;
|
||||||
import javax.money.convert.CurrencyConversion;
|
import javax.money.convert.CurrencyConversion;
|
||||||
import javax.money.convert.ExchangeRate;
|
import javax.money.convert.ExchangeRate;
|
||||||
import javax.money.convert.ExchangeRateProvider;
|
import javax.money.convert.ExchangeRateProvider;
|
||||||
import javax.money.convert.ProviderContext;
|
import javax.money.convert.ProviderContext;
|
||||||
|
import javax.money.convert.ProviderContextBuilder;
|
||||||
|
import javax.money.convert.RateType;
|
||||||
|
import org.javamoney.moneta.convert.ExchangeRateBuilder;
|
||||||
|
import org.javamoney.moneta.spi.DefaultNumberValue;
|
||||||
|
import org.javamoney.moneta.spi.LazyBoundCurrencyConversion;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Bonus exercise 3 for implementing local exchange rates between CHF and DukePoints.</p>
|
* <p>Bonus exercise 3 for implementing local exchange rates between CHF and DukePoints.</p>
|
||||||
*
|
*
|
||||||
* <p>The provider should support deterministic conversion for {@code CHF -> DKP} and {@code DKP -> CHF}.</p>
|
* <p>The provider should support deterministic conversion for {@code CHF -> DKP} and {@code DKP -> CHF}.</p>
|
||||||
*
|
|
||||||
* <p>The implementation is intentionally incomplete and should be finished by making tests pass.</p>
|
|
||||||
*/
|
*/
|
||||||
public class DukePointsExchangeRateProvider implements ExchangeRateProvider {
|
public class DukePointsExchangeRateProvider implements ExchangeRateProvider {
|
||||||
|
|
||||||
|
private static final BigDecimal CHF_TO_DKP_FACTOR = new BigDecimal("0.1");
|
||||||
|
private static final BigDecimal DKP_TO_CHF_FACTOR = new BigDecimal("10");
|
||||||
|
private static final ProviderContext CONTEXT =
|
||||||
|
ProviderContextBuilder.of("dkp-exchange-rate-provider", RateType.HISTORIC).build();
|
||||||
|
private static final String CHF = "CHF";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Create a new exchange rate provider for DukePoints.</p>
|
* <p>Create a new exchange rate provider for DukePoints.</p>
|
||||||
*/
|
*/
|
||||||
|
|
@ -28,7 +40,7 @@ public class DukePointsExchangeRateProvider implements ExchangeRateProvider {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public ProviderContext getContext() {
|
public ProviderContext getContext() {
|
||||||
throw new UnsupportedOperationException("TODO");
|
return CONTEXT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -41,7 +53,19 @@ public class DukePointsExchangeRateProvider implements ExchangeRateProvider {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public ExchangeRate getExchangeRate(ConversionQuery conversionQuery) {
|
public ExchangeRate getExchangeRate(ConversionQuery conversionQuery) {
|
||||||
throw new UnsupportedOperationException("TODO");
|
String baseCurrencyCode = conversionQuery.getBaseCurrency().getCurrencyCode();
|
||||||
|
String termCurrencyCode = conversionQuery.getCurrency().getCurrencyCode();
|
||||||
|
|
||||||
|
BigDecimal factor = resolveFactor(baseCurrencyCode, termCurrencyCode);
|
||||||
|
if (factor == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ExchangeRateBuilder(getContext().getProviderName(), RateType.HISTORIC)
|
||||||
|
.setBase(conversionQuery.getBaseCurrency())
|
||||||
|
.setTerm(conversionQuery.getCurrency())
|
||||||
|
.setFactor(DefaultNumberValue.of(factor))
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -54,6 +78,27 @@ public class DukePointsExchangeRateProvider implements ExchangeRateProvider {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public CurrencyConversion getCurrencyConversion(ConversionQuery conversionQuery) {
|
public CurrencyConversion getCurrencyConversion(ConversionQuery conversionQuery) {
|
||||||
throw new UnsupportedOperationException("TODO");
|
if (getExchangeRate(conversionQuery) == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
ConversionContext conversionContext = ConversionContextBuilder.create(getContext(), RateType.HISTORIC).build();
|
||||||
|
return new LazyBoundCurrencyConversion(conversionQuery, this, conversionContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Resolve the conversion factor for a supported DukePoints currency pair.</p>
|
||||||
|
*
|
||||||
|
* @param baseCurrencyCode currency code of the base currency
|
||||||
|
* @param termCurrencyCode currency code of the target currency
|
||||||
|
* @return conversion factor for supported pairs, otherwise {@code null}
|
||||||
|
*/
|
||||||
|
private BigDecimal resolveFactor(String baseCurrencyCode, String termCurrencyCode) {
|
||||||
|
if (CHF.equals(baseCurrencyCode) && DukePointsCurrencyFactory.DKP.equals(termCurrencyCode)) {
|
||||||
|
return CHF_TO_DKP_FACTOR;
|
||||||
|
}
|
||||||
|
if (DukePointsCurrencyFactory.DKP.equals(baseCurrencyCode) && CHF.equals(termCurrencyCode)) {
|
||||||
|
return DKP_TO_CHF_FACTOR;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue